互斥锁
简介
互斥锁是一种任务间互斥的机制,一个任务占有了某个资源,就不允许别的任务去访问,直到占有资源的任务释放锁。即一个资源同时只允许一个访问者对其访问,具有唯一性和排他性,但互斥不会限制访问者对资源的访问顺序,即访问是无序的。
本操作系统的互斥锁支持非递归锁和递归锁两种形式。当互斥锁设置为非递归锁时,一旦该锁被某个任务获取,在释放之前不能被任何任务再次获取;当互斥锁设置为递归锁时,若锁被某个任务获取,那么该任务可以再次获取这个锁而不会被挂起。一般情况下,使用者在使用锁时,应该明确自己要保护的临界资源的范围,只在对临界资源访问时加锁,访问完成后立即解锁。对临界资源的访问,经过合理设计后,一般都可以使用非递归锁实现;递归锁在某些错综复杂的调用关系情况下,使用起来比较方便,但是容易隐藏代码中可能存在的问题。
在处理互斥场景时,如果使用信号量,可能会存在任务优先级反转问题,即当一个高优先级任务尝试通过信号量机制访问临界资源时,如果该信号量已被低优先级任务持有,而此低优先级任务运行过程中可能被其它中优先级任务抢占,可能导致高优先级任务长时间被阻塞,实时性难以保证。而互斥锁考虑到这种场景,会动态提升持有锁的任务的优先级,使其不被中优先级任务抢占,尽快完成对临界资源的访问然后释放锁,解决了优先级反转的问题。
重要定义及数据结构
结构体
struct os_mutex
{
os_ipc_object_t parent; /* Inherit from os_ipc_object. */
os_uint8_t original_priority; /* Priority of last task lock the mutex. */
os_uint8_t nested; /* Number of times current owner lock the mutex. */
os_bool_t recursive; /* Support recursive call. */
os_task_t *owner; /* Current owner of the mutex. */
};
struct os_mutex重要参数 | 说明 |
---|---|
original_priority | 持有该锁的任务的原始优先级 |
nested | 持有该锁的任务的持有次数 |
recursive | 该锁是否支持递归调用,若为TRUE,则持有该锁的任务可以再次获取该锁 |
owner | 锁的当前持有者 |
API列表
接口 | 说明 |
---|---|
os_mutex_init | 以静态方式初始化互斥锁,即互斥锁对象由使用者提供;recursive为OS_FALSE时,为非递归锁;recursive为OS_TRUE时,为递归锁。对于非递归锁,只能被获取一次,只有该锁被释放后才能再次被获取;而对于递归锁,若某个任务已经获得锁,在释放该锁之前,该任务仍然可以多次获得该锁 |
os_mutex_deinit | 反初始化互斥锁,与os_mutex_init()配对使用 |
os_mutex_create | 以动态方式创建互斥锁并初始化,即互斥锁对象的空间采用动态申请内存的方式获取;recursive为OS_FALSE时,为非递归锁;recursive为OS_TRUE时,为递归锁。对于非递归锁,只能被获取一次,只有该锁被释放后才能再次被获取;而对于递归锁,若某个任务已经获得锁,在释放该锁之前,该任务仍然可以多次获得该锁 |
os_mutex_destroy | 销毁互斥锁,与os_sem_create()匹配使用 |
os_mutex_lock | 获取(非递归)锁 |
os_mutex_unlock | 释放(非递归)锁 |
os_mutex_recursive_lock | 获取(递归)锁 |
os_mutex_recursive_unlock | 释放(递归)锁 |
os_mutex_control | 控制或更改互斥锁的行为属性 |
os_mutex_init
该函数以静态方式初始化互斥锁,即互斥锁对象所使用的空间由使用者提供,函数原型如下:
os_err_t os_mutex_init(os_mutex_t *mutex, const char *name, os_ipc_flag_t flag, os_bool_t recursive);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
name | 互斥锁名字 |
flag | 标志,可以取OS_IPC_FLAG_FIFO和OS_IPC_FLAG_PRIO;当取值为OS_IPC_FLAG_FIFO时,等待的任务将按照先进先出的方式排队,先进入的任务先获得互斥锁;当取值为OS_IPC_FLAG_PRIO时,等待的任务将按照任务优先级排队,优先级高的任务先获得互斥锁 |
recursive | 表明是否为递归锁。若为OS_FALSE,为非递归锁,只能被获取一次,只有该锁被释放后才能再次被获取;若为OS_TRUE时,为递归锁,即某个任务已经获得锁,在释放该锁之前,该任务仍然可以多次获得该锁 |
返回 | 说明 |
OS_EOK | 初始化成功 |
os_mutex_deinit
该函数反初始化互斥锁,与os_mutex_init()配合使用,函数原型如下:
os_err_t os_mutex_deinit(os_mutex_t *mutex);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
返回 | 说明 |
OS_EOK | 成功 |
os_mutex_create
该函数以动态方式创建并初始化互斥锁,即互斥锁对象使用的内存空间通过动态申请获得,函数原型如下:
os_mutex_t *os_mutex_create(const char *name, os_ipc_flag_t flag, os_bool_t recursive);
参数 | 说明 |
---|---|
name | 互斥锁名字 |
flag | 标志,可以取OS_IPC_FLAG_FIFO和OS_IPC_FLAG_PRIO;当取值为OS_IPC_FLAG_FIFO时,等待的任务将按照先进先出的方式排队,先进入的任务先获得互斥锁;当取值为OS_IPC_FLAG_PRIO时,等待的任务将按照任务优先级排队,优先级高的任务先获得互斥锁 |
recursive | 表明是否为递归锁。若为OS_FALSE,为非递归锁,只能被获取一次,只有该锁被释放后才能再次被获取;若为OS_TRUE时,为递归锁,即某个任务已经获得锁,在释放该锁之前,该任务仍然可以多次获得该锁 |
返回 | 说明 |
非OS_NULL | 创建成功,返回互斥锁句柄 |
OS_NULL | 创建失败 |
os_mutex_destroy
该函数销毁互斥锁,并释放互斥锁对象所占用的内存空间,与os_mutex_create()配合使用,原型如下:
os_err_t os_mutex_destroy(os_mutex_t *mutex);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
返回 | 说明 |
OS_EOK | 成功 |
os_mutex_lock
该函数用于获取(非递归)互斥锁,若暂时获取不到锁且设定了超时时间,则当前任务会阻塞,函数原型如下:
os_err_t os_mutex_lock(os_mutex_t *mutex, os_tick_t timeout);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄,该锁未被释放前,不能再次被获取 |
timeout | 互斥锁暂时获取不到时的等待超时时间。若为OS_IPC_WAITING_NO,则直接返回OS_EBUSY;若为OS_IPC_WAITING_FOREVER,则永久等待直到获取到互斥锁;若为其它值,则等待timeout时间或者获取到互斥锁为止 |
返回 | 说明 |
OS_EOK | 获取互斥锁成功 |
OS_EBUSY | 不等待且未获取到互斥锁 |
OS_ETIMEOUT | 等待超时未获取到互斥锁 |
OS_ERROR | 其它错误 |
os_mutex_unlock
该函数用于释放(非递归)互斥锁,与os_mutex_lock()配合使用,函数原型如下:
os_err_t os_mutex_unlock(os_mutex_t *mutex);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
返回 | 说明 |
OS_EOK | 释放成功 |
os_mutex_recursive_lock
该函数用于获取(递归)互斥锁,函数原型如下:
os_err_t os_mutex_recursive_lock(os_mutex_t *mutex, os_tick_t timeout);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄,该锁未被释放前,可以被已经持有该锁的任务再次获取 |
timeout | 互斥锁暂时获取不到时的等待超时时间。若为OS_IPC_WAITING_NO,则直接返回OS_EBUSY;若为OS_IPC_WAITING_FOREVER,则永久等待直到获取到互斥锁;若为其它值,则等待timeout时间或者获取到互斥锁为止 |
返回 | 说明 |
OS_EOK | 获取互斥锁成功 |
OS_EBUSY | 不等待且未获取到互斥锁 |
OS_ETIMEOUT | 等待超时未获取到互斥锁 |
OS_ERROR | 其它错误 |
os_mutex_recursive_unlock
该函数用于释放(递归)互斥锁,与os_mutex_recursive_lock()配套使用,函数原型如下:
os_err_t os_mutex_recursive_unlock(os_mutex_t *mutex);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
返回 | 说明 |
OS_EOK | 释放成功 |
OS_ERROR | 释放错误 |
os_mutex_control
该函数用于控制和改变互斥锁的属性,目前暂无支持的命令,函数原型如下:
os_err_t os_mutex_control(os_mutex_t *mutex, os_ipc_cmd_t cmd, void *arg);
参数 | 说明 |
---|---|
mutex | 互斥锁句柄 |
cmd | 命令,目前暂无支持的命令 |
arg | 不同命令时参数含义不同 |
返回 | 说明 |
OS_ERROR | 错误 |
使用示例
静态互斥锁使用示例
本例采用静态方式初始化了一个(非递归)互斥锁,并在两个任务里面都去访问两个全局变量,使用互斥锁对这两个全局变量进行保护
#include <oneos_config.h>
#include <os_dbg.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <os_mutex.h>
#define TEST_TAG "TEST"
#define TASK_STACK_SIZE 1024
#define TASK1_PRIORITY 15
#define TASK2_PRIORITY 16
#define TASK_TIMESLICE 10
static os_uint32_t count1 = 0;
static os_uint32_t count2 = 0;
static os_mutex_t mutex_static;
void task1_entry(void *para)
{
while (1)
{
if (OS_EOK == os_mutex_lock(&mutex_static, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task1 mutex lock");
}
else
{
LOG_W(TEST_TAG, "task1 mutex lock err");
}
count1++;
LOG_W(TEST_TAG, "task1 sleep");
os_task_sleep(100);
count2++;
if (OS_EOK == os_mutex_unlock(&mutex_static))
{
LOG_W(TEST_TAG, "task1 mutex unlock");
}
else
{
LOG_W(TEST_TAG, "task1 mutex unlock err");
}
os_task_sleep(500);
}
}
void task2_entry(void *para)
{
while (1)
{
if (OS_EOK == os_mutex_lock(&mutex_static, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task2 mutex lock");
}
else
{
LOG_W(TEST_TAG, "task2 mutex lock err");
}
LOG_W(TEST_TAG, "task2 count1:%d count2:%d", count1, count2);
count1++;
count2++;
if (OS_EOK == os_mutex_unlock(&mutex_static))
{
LOG_W(TEST_TAG, "task2 mutex unlock");
}
else
{
LOG_W(TEST_TAG, "task2 mutex unlock err");
}
os_task_sleep(500);
}
}
void mutex_static_sample(void)
{
os_task_t *task1 = OS_NULL;
os_task_t *task2 = OS_NULL;
os_mutex_init(&mutex_static, "mutex_static", OS_IPC_FLAG_FIFO, OS_FALSE);
task1 = os_task_create("task1",
task1_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK1_PRIORITY,
TASK_TIMESLICE);
if (task1)
{
os_task_startup(task1);
}
task2 = os_task_create("task2",
task2_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK2_PRIORITY,
TASK_TIMESLICE);
if (task2)
{
os_task_startup(task2);
}
}
SH_CMD_EXPORT(static_mutex, mutex_static_sample, "test staitc mutex");
运行结果如下:
sh />static_mutex
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:1 count2:1
W/TEST: task2 mutex unlock
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:3 count2:3
W/TEST: task2 mutex unlock
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:5 count2:5
W/TEST: task2 mutex unlock
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:7 count2:7
W/TEST: task2 mutex unlock
动态互斥锁使用示例
本例采用动态方式创建并初始化了(递归)互斥锁,并在两个任务里面都去访问两个全局变量,使用互斥锁对这两个全局变量进行保护。本例中的递归互斥锁的使用没有实际意义,仅为演示递归锁的使用方法及效果
#include <oneos_config.h>
#include <os_dbg.h>
#include <os_errno.h>
#include <os_task.h>
#include <shell.h>
#include <os_mutex.h>
#define TEST_TAG "TEST"
#define TASK_STACK_SIZE 1024
#define TASK1_PRIORITY 15
#define TASK2_PRIORITY 16
#define TASK_TIMESLICE 10
#define TEST_RECURSIVE_CNT 3
static os_uint32_t count1 = 0;
static os_uint32_t count2 = 0;
static os_mutex_t *mutex_dynamic = OS_NULL;
void task1_entry(void *para)
{
os_uint32_t i = 0;
while (1)
{
for (i = 0; i < TEST_RECURSIVE_CNT; i++)
{
if (OS_EOK == os_mutex_recursive_lock(mutex_dynamic, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task1 mutex lock");
count1++;
LOG_W(TEST_TAG, "task1 sleep");
os_task_sleep(100);
count2++;
}
else
{
LOG_W(TEST_TAG, "task1 mutex lock err");
}
}
for (i = 0; i < TEST_RECURSIVE_CNT; i++)
{
if (OS_EOK == os_mutex_recursive_unlock(mutex_dynamic))
{
LOG_W(TEST_TAG, "task1 mutex unlock");
}
else
{
LOG_W(TEST_TAG, "task1 mutex unlock err");
}
}
os_task_sleep(500);
}
}
void task2_entry(void *para)
{
while (1)
{
if (OS_EOK == os_mutex_recursive_lock(mutex_dynamic, OS_IPC_WAITING_FOREVER))
{
LOG_W(TEST_TAG, "task2 mutex lock");
}
else
{
LOG_W(TEST_TAG, "task2 mutex lock err");
}
LOG_W(TEST_TAG, "task2 count1:%d count2:%d", count1, count2);
count1++;
count2++;
if (OS_EOK == os_mutex_recursive_unlock(mutex_dynamic))
{
LOG_W(TEST_TAG, "task2 mutex unlock");
}
else
{
LOG_W(TEST_TAG, "task2 mutex unlock err");
}
os_task_sleep(500);
}
}
void mutex_dynamic_sample(void)
{
os_task_t *task1 = OS_NULL;
os_task_t *task2 = OS_NULL;
mutex_dynamic = os_mutex_create("mutex_dynamic", OS_IPC_FLAG_FIFO, OS_TRUE);
task1 = os_task_create("task1",
task1_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK1_PRIORITY,
TASK_TIMESLICE);
if (task1)
{
os_task_startup(task1);
}
task2 = os_task_create("task2",
task2_entry,
OS_NULL,
TASK_STACK_SIZE,
TASK2_PRIORITY,
TASK_TIMESLICE);
if (task2)
{
os_task_startup(task2);
}
}
SH_CMD_EXPORT(dynamic_mutex, mutex_dynamic_sample, "test dynamic mutex");
运行结果如下,可以看到task1获得递归互斥锁之后,仍然可以再次获得递归互斥锁,而task2必须等task1把递归互斥锁完全释放完之后才能获取到锁:
sh />dynamic_mutex
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:3 count2:3
W/TEST: task2 mutex unlock
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
W/TEST: task2 mutex lock
W/TEST: task2 count1:7 count2:7
W/TEST: task2 mutex unlock
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex lock
W/TEST: task1 sleep
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
W/TEST: task1 mutex unlock
[W/TEST: task2 mutex lock
[W/TEST: task2 count1:11 count2:11
W/TEST: task2 mutex unlock