信号量由信号量计数器和等待任务表两部分组成。信号量使用事件控制块OSEventCnt作为计数器,而使用数组OSEventTbl[]来充当等待任务表。 每当有任务申请信号量时,如果信号量计数器OSEventCnt的值大于0,则把OSEventCnt减1并使任务继续运行;如果OSEventCnt的值为0,则会将任务列入任务等待表OSEventTbl[],使任务处于等待状态。如果有正在使用的信号量的任务释放了信号量,则会在任务等待表中找出优先级别最高的等待任务,并在使它就绪后调用调度器引发一次调度;如果任务等待表中已经没有等待任务,则信号量计数器就只简单地加1.
图4-10 是一个计数器当前值为3且有4个等待任务的信号量的示意图。
信号量的操作创建信号量
OS_EVENT * OSSemCreate(
INT16U cnt //信号量计数器值
)
注意:信号量计数器值要看允许用户定义最大事件数函数的返回值为已创建的信号量的指针。OSSemCreate()的源码如下:
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (OSIntNesting > 0) { /* 是否产生中断服务函数*/
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /*获取下一个空事件控制块指针*/
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_SEM; //设置为信号量
pevent->OSEventCnt = cnt; //置计数器初值
pevent->OSEventPtr = (void *)0; //置空指针
OS_EventWaitListInit(pevent); //初始化控制块
}
return (pevent);
}
请求信号量函数OSSemPend()原型如下:
void OSSemPend()
{
OS_EVENT *pevent, //信号量指针
INT16U timeout, //等待时限
INT8U *err //错误信息
}
参数timeout被设置为0,则表明任务的等待时间为无限长;被设置为不为0,则表明任务的等待时间。
如果信号量有效(即信号量的OSEventCnt大于0),则把信号量计数器减1,然后任务继续运行;若信号量无效(即信号量的OSEventCnt等于0),则会在等待任务表中把该任务对应的位设置为1而让任务处于等待状态,并把等待时限timeout保存在任务控制块TCB的成员OSTCBDly中。
当一个任务请求信号量时,如果希望在信号量无效时准许任务不进入等待状态而继续运行,则不调用OSSemPend(),而是调用函数OSSemAccept()来请求信号量。该函数原型如下:
INT16U OSSemAccept(OS_EVENT * pevent);
发送信号量函数OSSemPost()在对信号量的计数器操作之前,首先检查是否还有等待该信号量的任务;如果没有,则将信号量计数器OSEventCnt加1;如果有,则调用调度器OS_Sched()去运行等待任务中优先级别最高的任务。(考虑一个信号量在多个任务中调用OSSemPend()的情况)其函数原型如下:
INT8U OSSemPost(
OS_EVENT *pevent
);
删除信号量调用函数OSSemDel()来删除信号量。该函数的原型如下:
OS_EVENT * OSSemDel(
OS_EVENT *pevent, //信号量指针
INT8U opt, //删除条件
INT8U *err //错误信息
)
函数中的参数opt用来指明信号量的删除条件。该参数有两个值选择OS_DEL_NO_PEND,则当等待任务表中已没有等待任务时才删除信号量;OS_DEL_ALLWAYS,则表明在等待任务表中无论是否有等待任务都立即删除信号量。
注意:只能在任务中删除信号量,而不能在中断服务程序中删除。
查询信号量任务可以调用函数OSSemQuery(随时查询信号量的当前状态。该函数的原型如下:
INT8U OSSemQuery(
OS_EVENT *pevent,
OS_SEM_DATA *pdata //存储信号量状态的结构
)
第二个参数pdata是一个OS_SEM_DATA结构的指针。OS_SEM_DATA的结构如下:
typedef struct {
INT16U OSCnt,
INT8U OSEventTbl[OS_EVENT_TBL_SIZE],
INT8U OSEventGrp
}OS_SEM_DATA;
任务在调用此函数之后,会把信号量中的相关信息存储到OS_SEM_DATA类型的变量中,因此在调用函数之前,须定义一个OS_SEM_DATA结构类型的变量。