信号量
更新时间:2019-01-25 14:10:45
概述
对于多任务,甚至多核的操作系统,需要访问共同的系统资源。共享资源包括软件资源和硬件资源,软件共享资源主要在于共享内存,包括共享变量、共享队列等等,硬件共享资源包括一些硬件设备的访问,例如:输入\输出设备,打印机等。为了避免软件访问共享资源的读写发生的相互影响甚至冲突,一般在保护共享资源时,有下列几种处理方式:开关中断、信号量(semphore)、互斥量(mutex)。
开关中断:一般用于单核内多任务之间的互斥,其途径在于关闭任务的调度切换,从而达到单任务访问共享资源的目的。缺点是会影响实际的中断调度效率;
信号量:多任务可以通过获取信号量来获取访问共享资源的“门禁”,可以配置信号量数目,让多个任务同时获取“门禁”,当信号量无法获取时,相关任务会按照优先级排序等待信号量释放,并让出cpu资源;缺点是存在高低任务优先级反转的问题,见后续本节描述;
互斥量:任务也是通过获取mutex来获取访问共享资源的门禁,但是单此只有一个任务能获取到该互斥量。互斥量通过动态调整任务的优先级来解决高低优先级反转的问题。具体见下一章节。
本章节介绍信号量semphore。
API 列表
AOS API | 说明 |
---|---|
aos_sem_new() | 动态创建信号量 |
aos_sem_free() | 删除信号量 |
aos_sem_wait() | 信号量获得 |
aos_sem_signal() | 释放一个sem信号量 |
aos_sem_is_valid() | 判断信号量是否有效 |
aos_sem_signal_all() | 释放一个sem信号量,并唤醒所有阻塞任务 |
API 详情
aos_sem_new()
定义描述
函数原型
|
int aos_sem_new(aos_sem_t *sem, int count)
|
描述
|
动态创建一个sem信号量
|
入参
|
sem:信号量结构体指针;需要用户定义一个sem结构体
|
count:此sem的初始计数值,此值大于0,才能获取到信号量,获取一次,count计数减1;
|
|
返回值
|
类型:int 返回成功或失败;返回0表示timer创建成功,非0表示失败。
|
aos_sem_free()
定义描述
函数原型 | void aos_sem_free(aos_sem_t *sem) |
---|---|
描述 | 删除信号量 |
入参 | sem:信号量结构体指针 |
返回值 | 无 |
aos_sem_signal()
定义描述
函数原型 | void aos_sem_signal(aos_sem_t *sem) |
---|---|
描述 | 释放一个sem信号量,并唤醒一个高优先级阻塞任务 |
入参 | sem:信号量结构体指针 |
返回值 | 无 |
aos_sem_signal_all()
定义描述
函数原型 | void aos_sem_signal(aos_sem_t *sem) |
---|---|
描述 | 释放一个sem信号量,并唤醒所有阻塞任务 |
入参 | sem:信号量结构体指针 |
返回值 | 无 |
aos_sem_wait()
定义描述
函数原型
|
int aos_sem_wait(aos_sem_t *sem, unsigned int timeout)
|
描述
|
获取一个sem信号量;获取不到信号量,当前任务阻塞
|
入参
|
sem:信号量结构体指针
|
Timeout:传入0表示不超时,立即返回;AOS_WAIT_FOREVER表示永久等待;
其他数值表示超时时间,单位ms
|
|
返回值
|
类型:int 返回成功或失败;返回0表示信号量创建成功,非0表示失败(包括超时返回RHINO_BLK_TIMEOUT)
|
aos_sem_is_valid()
定义描述
函数原型 | int aos_sem_is_valid(aos_sem_t *sem) |
---|---|
描述 | 判断一个信号量sem是否有效 |
入参 | sem:信号量结构体指针 |
返回值 | 类型:int 返回1表示有效,0表示无效 |
调用示例
当前任务创建一个信号量和子任务,并等待子任务释放信号量
static aos_sem_t g_sem_taskexit_sync;
unsigned int stack_size = 1024;
int ret = -1;
....
static void task1(void *arg)
{
....
/*释放信号量*/
aos_sem_signal(&g_sem_taskexit_sync);
....
}
/*当前任务:创建信号量,信号量初始count为0*/
ret = aos_sem_new(&g_sem_taskexit_sync, 0);
if (ret != 0) {
printf("sem create failed\r\n");
...
}
....
/*判断信号量是否可用*/
ret = aos_sem_is_valid(&g_sem_taskexit_sync);
if (ret == 0) {
printf("sem is invalid\r\n");
...
}
/*创建新任务task1*/
ret = aos_task_new("task1", task1, NULL, stack_size);
if (ret != 0) {
printf("timer create failed\r\n");
...
}
....
/*获取信号量,由于初始值为0,这里获取不到信号量,当前任务进入睡眠并发生切换
参数 -1 表示AOS_WAIT_FOREVER,直到task1任务释放信号量*/
aos_sem_wait(&g_sem_taskexit_sync, -1);
/*获取到信号量,当前任务继续执行下去*/
printf("task1 exit!\r\n");
....
/*删除信号量*/
aos_sem_free(&g_sem_taskexit_sync);
使用注意事项
1)在中断中禁止信号量获取检测
信号量的获取接口在中断上下文调用很容易发生死锁问题。当被打断的上下文和打断的中断上下文要获取同一个信号量时,会发生互相等待的情况。有些内核将这种判断处理交由上层软件进行判断和使用,本内核会在take信号量检测,如果是中断上下文,则直接返回失败。
2) 占用信号量非等待、永远等待、延时使用区别
上层应用在获取信号量时,需要按照实际的需求,来安排信号量获取策略。krhino_sem_take
传入延时ticks为0,获取不到信号量会立即报失败;ticks为全F时,会永远在此等待,直到获取到信号量,可能会造成该任务无法继续运行;其他值标识最大延迟的时间上限,达到上限时,及时未获取到信号量,tick中断处理会将任务唤醒,并返回状态为超时。
3) 信号量优先级反转问题
优先级反转出现在高、中、低三个优先级任务同时访问使用信号量互斥资源时,可能存在的问题。当高优先级的任务需要的信号量被低优先级任务占用时,cpu资源会调度给低优先级任务。此时如果低优先级需要获取的另一个信号量被中优先级的pend任务所占用,那么低优先级的任务则需要等待中优先级的任务事件到来,并释放信号量,则就出现了高、中优先级的任务并不是等待一个信号量,但是中优先级任务先运行的现象。
该优先级反转的缺陷,在互斥量mutex得以解决,其途径在于动态提高C任务运行优先级来避免任务优先级的反转问题,详细内容见下一章节。
4 ) 信号量整体受位于k_config.h
中的宏 RHINO_CONFIG_KOBJ_DYN_ALLOC
和 RHINO_CONFIG_SEM
开关控制。