一. 信号量的概念
信号量的最初目的,是为了给共享资源设立一个标志,该标志表示该共享资源被占用的情况。
当事件控制块成员OSEventType的值被设置成OS_EVENT_TYPE_SEM时,这个事件控制块描述的就是一个信号量。
信号量由信号量计数器和等待任务表两部分组成:
信号量使用事件控制块的成员OSEventCnt作为计数器,而用数组OSEventTb1[]来充当等待任务表。
工作时有下面几种情况:
1. 每当有任务申请信号量时,如果信号量计数器OSEventCnt的值大于0,则把OSEventCnt减1并使任务继续运行。
2. 如果信号量计数器OSEventCnt的值等于0,则会将任务列入任务等待表OSEventTb1[],而使任务处于等待状态。
3. 如果有正在使用信号量的任务释放了该信号量,则会在任务等待表中找出优先级别最高的等待任务,并在使它就绪后调用调度器引发一次调度。
4. 如果任务等待表中已经没有等待任务,则信号量计数器OSEventCnt就简单地加1。
二. 信号量的操作
OS_EVENT *OSSemCreate (INT16U cnt); //信号量计数器初值
如创建一个计数器初值为10的信号量的示意图如下:
2. 请求信号量
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err);
如果信号量有效,即信号量的计数器
OSEventCnt > 0,则把OSEventCnt减1并使任务继续运行。
如果信号量无效,即信号量的计数器 OSEventCnt = 0,则会在等待任务表中把该任务对应的位置1而让任务处于等待状态,并把等待时限timeout保存在任务控制块TCB的成员OSTCBDly中。
当一个任务请求信号时,如果希望在信号量无效时准许任务不进入等待状态而继续运行,则不调用OSSemPend(),而是调用下面的函数来实现:
INT16U OSSemAccept (OS_EVENT *pevent);
3. 发送信号量
INT8U OSSemPost (OS_EVENT *pevent)
任务获得信号量,并在访问共享资源结束后,必须释放信号量,也叫发送信号量。
他的过程是:首先检查是否还有等待该信号量的任务,如果有调用调度器OS_Sched();如果没有,则把计数器OSEventCnt加1。
三. 信号量举例
//定义信号量
Fun_Semp = OSSemCreate(1); /* 定义信号量 */
//MyTask任务
void MyTask(void *pdata)
{
for (;;)
{
OSSemPend(Fun_Semp, 0, &err); /* 申请信号量,会在此一直等待,至到申请到 */
/* 读写共享资源 */
OSSemPost(Fun_Semp); /* 释放信号量 */
OSTimeDlyHMSM(0,0,1,0); /* 等待1S */
}
}
//YouTask任务
void YouTask(void *pdata)
{
for (;;)
{
OSSemPend(Fun_Semp, 0, &err); /* 申请信号量,会在此一直等待,至到申请到 */
/* 读写共享资源 */
OSSemPost(Fun_Semp); /* 释放信号量 */
OSTimeDlyHMSM(0,0,2,0); /* 等待2S */
}
}
说明:
因为只定义一个信号量,所以一旦任何一个任务调用了OSSemPend()后,就会使得OSEventCnt=0,这时其他任务再调用OSSemPend()后就会等待。直到那个任务释放OSSemPost了信号量,使OSEventCnt=1后其它任务才能使用, 从而实现了互斥访问资源的同步。
例2:MyTask必须经过任务YouTask同意才能使用一个共享资源:
//定义一个初值为0的信号量
Fun_Semp = OSSemCreate(0); /* 注意!!!定义一个初值为0的信号量 */
//MyTask任务
void MyTask(void *pdata)
{
for (;;)
{
OSSemPend(Fun_Semp, 0, &err); /* 申请信号量,会在此一直等待,至到申请到 */
/* 读写共享资源 */
OSTimeDlyHMSM(0,0,1,0); /* 等待1S */
}
}
//YouTask任务
void YouTask(void *pdata)
{
for (;;)
{
/* 工作一段时间,条件成熟后发信号通知 MyTask任务*/
OSSemPost(Fun_Semp); /* 释放信号量 */
OSTimeDlyHMSM(0,0,2,0); /* 等待2S */
}
}
说明:
因为只定义一个初值为0的信号量(OSEventCnt=0),所以开始时MyTask不能工作只能等待,一旦YouTask任务调用了OSSemPend()后,就会使得OSEventCnt=1,MyTask得到了信号量,继续向下执行, 从而实现了执行上有先后次序的同步。