互斥锁
2026/7/16大约 6 分钟内核组件内核同步互斥锁优先级继承
互斥锁
📚 本节导读
学习时长: 约 30 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: 任务管理、优先级、信号量基础
互斥锁(Mutex,Mutual Exclusion)是保护共享资源免受并发访问破坏的核心机制。与信号量不同,互斥锁具有所有权概念,只有持有锁的任务才能释放锁,并且 OneOS 实现了优先级继承来防止优先级反转。
🎯 学习目标
- 理解互斥锁与信号量的本质区别
- 掌握互斥锁的 lock/unlock 操作
- 理解优先级反转问题及优先级继承的解决方案
- 掌握递归互斥锁的使用场景
- 能够正确使用互斥锁保护共享资源
一、互斥锁概念
1.1 什么是互斥锁
互斥锁是一种拥有所有权的同步对象。同一时刻只有一个任务可以持有锁,只有持有锁的任务才能释放锁。
┌─────────────────────────────────────────────────────────────────┐
│ 互斥锁模型 │
│ │
│ ┌──────────┐ lock ┌──────────────────┐ │
│ │ 任务 A │ ─────────────────→│ 互斥锁 │ │
│ └──────────┘ 获取锁,进入临界区│ ┌────────────┐ │ │
│ │ │ │ owner: A │ │ │
│ ▼ │ │ recursive: │ │ │
│ ┌──────────┐ 临界区执行 │ │ count: 1 │ │ │
│ │ 共享资源 │ ←─────────────────│ │ wait_list │ │ │
│ └──────────┘ │ └────────────┘ │ │
│ │ └──────────────────┘ │
│ ▼ │
│ ┌──────────┐ unlock │
│ │ 任务 A │ ─────────────────→ 释放锁,唤醒等待者 │
│ └──────────┘ │
│ │
│ ┌──────────┐ lock (失败) │
│ │ 任务 B │ ─────────────────→ 进入等待队列,阻塞 │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘1.2 互斥锁 vs 信号量
| 特性 | 互斥锁 | 信号量 |
|---|---|---|
| 所有权 | 有,只有持有者可以释放 | 无,任何任务/中断都可以释放 |
| 优先级继承 | 支持 | 不支持 |
| 递归持有 | 支持(可选) | 不支持 |
| 中断中使用 | 否 | 仅 post 可以 |
| 典型用途 | 保护共享资源 | 资源计数、任务同步 |
1.3 优先级反转与优先级继承
优先级反转是实时系统中经典的并发问题。考虑以下场景:
优先级: 任务 H (高) > 任务 M (中) > 任务 L (低)
时间 →
───────────────────────────────────────────────────────────────
任务 H: [就绪] [等待锁] [获得锁][临界区]
任务 M: [就绪][执行中...........][完成]
任务 L: [获得锁][临界区......] [释放锁]
───────────────────────────────────────────────────────────────- 任务 L 获得锁,进入临界区
- 任务 H 就绪,抢占任务 L,但需要锁,被阻塞
- 任务 M 就绪,抢占任务 L 执行(任务 M 不需要锁)
- 任务 H 被任务 M 无限期阻塞——优先级反转
OneOS 的解决方案:优先级继承
当任务 H 因等待锁而阻塞时,OneOS 将持有锁的任务 L 的优先级临时提升到任务 H 的优先级。这样任务 M 无法抢占任务 L,任务 L 可以快速完成临界区并释放锁,任务 H 即可获得锁。
启用优先级继承后:
───────────────────────────────────────────────────────────────
任务 H: [就绪] [等待锁] [获得锁][临界区]
任务 M: [就绪] [就绪.........] [执行]
任务 L: [获得锁][临界区(优先级提升到 H)][释放锁]
───────────────────────────────────────────────────────────────二、API 详解
以下 API 定义在 kernel/include/os_mutex.h 中:
#include <os_mutex.h>注意:使用互斥锁需要在
oneos_config.h中启用OS_USING_MUTEX宏。
2.1 静态定义
#define OS_MUTEX_DEFINE(name) os_mutex_dummy_t name2.2 创建互斥锁
os_mutex_id os_mutex_create(os_mutex_dummy_t *mutex_cb,
const char *name,
os_bool_t recursive);| 参数 | 说明 |
|---|---|
mutex_cb | 互斥锁控制块指针。静态创建时传入 OS_MUTEX_DEFINE 定义的变量地址;动态创建时传 OS_NULL |
name | 互斥锁名称 |
recursive | OS_TRUE 表示递归锁,OS_FALSE 表示普通锁 |
| 返回值 | 成功返回互斥锁 ID,失败返回 OS_NULL |
2.3 销毁互斥锁
os_err_t os_mutex_destroy(os_mutex_id mutex_id);2.4 加锁 / 解锁
os_err_t os_mutex_lock(os_mutex_id mutex_id, os_tick_t timeout);
os_err_t os_mutex_unlock(os_mutex_id mutex_id);| 函数 | 说明 |
|---|---|
os_mutex_lock | 获取互斥锁。timeout 为 OS_NO_WAIT 时非阻塞尝试;OS_WAIT_FOREVER 时永久等待 |
os_mutex_unlock | 释放互斥锁。只有持有锁的任务才能调用 |
2.5 递归锁
os_err_t os_mutex_recursive_lock(os_mutex_id mutex_id, os_tick_t timeout);
os_err_t os_mutex_recursive_unlock(os_mutex_id mutex_id);递归锁允许同一个任务多次获取同一个锁。每次 recursive_lock 都会增加内部计数,每次 recursive_unlock 减少计数,直到计数归零才真正释放锁。
2.6 查询函数
os_err_t os_mutex_set_wake_type(os_mutex_id mutex_id, uint8_t wake_type);
os_err_t os_mutex_get_owner(os_mutex_id mutex_id, os_task_id *tid);
os_bool_t os_mutex_check_exist(os_mutex_id mutex_id);
os_err_t os_mutex_is_recursive(os_mutex_id mutex_id, os_bool_t *is_recursive);| 函数 | 说明 |
|---|---|
os_mutex_set_wake_type | 设置唤醒策略:OS_MUTEX_WAKE_TYPE_PRIO 或 OS_MUTEX_WAKE_TYPE_FIFO |
os_mutex_get_owner | 获取当前持有锁的任务 ID |
os_mutex_check_exist | 检查互斥锁是否存在 |
os_mutex_is_recursive | 检查是否为递归锁 |
三、代码示例
3.1 基本互斥锁:保护共享资源
#include <oneos_config.h>
#include <os_task.h>
#include <os_mutex.h>
OS_MUTEX_DEFINE(g_shared_mutex);
/* 共享全局变量 */
static int g_shared_counter = 0;
void task_a(void *parameter)
{
os_mutex_id mutex_id;
/* 静态创建普通互斥锁(非递归) */
mutex_id = os_mutex_create(&g_shared_mutex, "shared_mutex", OS_FALSE);
while (1)
{
/* 获取锁 */
os_mutex_lock(mutex_id, OS_WAIT_FOREVER);
/* 临界区:安全地访问共享变量 */
g_shared_counter++;
printf("Task A: counter = %d\n", g_shared_counter);
/* 释放锁 */
os_mutex_unlock(mutex_id);
os_task_sleep(500);
}
}
void task_b(void *parameter)
{
os_mutex_id mutex_id = (os_mutex_id)parameter;
while (1)
{
os_mutex_lock(mutex_id, OS_WAIT_FOREVER);
g_shared_counter--;
printf("Task B: counter = %d\n", g_shared_counter);
os_mutex_unlock(mutex_id);
os_task_sleep(500);
}
}3.2 递归互斥锁
OS_MUTEX_DEFINE(g_recursive_mutex);
/* 递归函数示例:多次获取同一个锁 */
void recursive_function(os_mutex_id mutex_id, int depth)
{
if (depth <= 0)
return;
/* 递归加锁 */
os_mutex_recursive_lock(mutex_id, OS_WAIT_FOREVER);
printf("Recursive lock depth: %d\n", depth);
/* 递归调用 */
recursive_function(mutex_id, depth - 1);
/* 递归解锁 */
os_mutex_recursive_unlock(mutex_id);
}
void task_recursive(void *parameter)
{
os_mutex_id mutex_id;
/* 创建递归互斥锁 */
mutex_id = os_mutex_create(&g_recursive_mutex, "recursive_mutex", OS_TRUE);
/* 使用递归锁 */
recursive_function(mutex_id, 3);
}3.3 非阻塞尝试加锁
void task_trylock(void *parameter)
{
os_mutex_id mutex_id = (os_mutex_id)parameter;
while (1)
{
/* 尝试加锁,不等待 */
os_err_t ret = os_mutex_lock(mutex_id, OS_NO_WAIT);
if (ret == OS_EOK)
{
/* 成功获取锁 */
printf("Got the lock!\n");
os_mutex_unlock(mutex_id);
}
else
{
/* 锁被占用,执行其他操作 */
printf("Lock busy, doing other work...\n");
}
os_task_sleep(100);
}
}3.4 查询所有者
void task_query_owner(void *parameter)
{
os_mutex_id mutex_id = (os_mutex_id)parameter;
os_task_id owner;
os_bool_t is_recursive;
os_mutex_get_owner(mutex_id, &owner);
printf("Current owner task ID: %p\n", owner);
os_mutex_is_recursive(mutex_id, &is_recursive);
printf("Is recursive mutex: %s\n", is_recursive ? "YES" : "NO");
}📝 本节小结
互斥锁是保护共享资源的核心机制,关键要点:
- 所有权:互斥锁有所有者,只有持有者才能释放,这保证了互斥访问的完整性
- 优先级继承:OneOS 自动处理优先级反转,将持有锁的低优先级任务提升到等待者的优先级
- 递归锁:支持同一任务多次获取锁,适用于嵌套调用场景
- 不能在中断中使用:互斥锁会导致阻塞,中断中不能使用
- 保护临界区时,持有锁的时间应尽可能短,避免影响系统实时性