信号量是什么?信号量有什么用?
信号量一是可以用来表示一个或多个事件的发生,二是用来对共享资源的访问。
ucos II提供了5个对信号量进行操作的函数。它们是:
1. 建立一个信号量, OSSemCreate()
2. 等待一个信号量, OSSemPend()
3. 发送一个信号量, OSSemPost()
4. 无等待地请求一个信号量, OSSemAccept()
5. 查询一个信号量的当前状态, OSSemQuery()
OSSemCreate()的实现代码如下:
OS_EVENT *OSSemCreate (INT16U cnt) |
{ |
OS_EVENT *pevent; |
|
|
OS_ENTER_CRITICAL(); |
pevent = OSEventFreeList; (1) |
if (OSEventFreeList != (OS_EVENT *)0) { (2) |
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; |
} |
OS_EXIT_CRITICAL(); |
if (pevent != (OS_EVENT *)0) { (3) |
pevent->OSEventType = OS_EVENT_TYPE_SEM; (4) |
pevent->OSEventCnt = cnt; (5) |
OSEventWaitListInit(pevent); (6) |
} |
return (pevent); (7) |
} |
在mcu21看来,创建一个信号量,简单来说,就是申请一个事件控制块,接着初始化这个事件控制块。
首先,它从空闲任务控制块链表中得到一个事件控制块[ (1)],并对空闲事件控制链表的指针进行适当的调整,使它指向下一个空闲的事件控制块[ (2)]。如果这时有 事件 控制块可用[ (3)],就将该 事件 控制块的事件类型设置成信号量OS_EVENT_TYPE_SEM[ (4)]。其它的信号量操作函数OSSem???()通过检查该域来保证所操作的 事件控制块类型的正确。例如,这可以防止调用OSSemPost()函数对一个用作邮箱的 事件 控制块进行操作。
接着,用信号量的初始值对 事件 控制块进行初始化[(5)](如果信号量是用来表示一个或者多个事件的发生,那么该信号量的初始值应设为0, 如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1.),并调用OSEventWaitListInit()函数对事件控制任务控制块的等待任务列表进行初始化 [(6)]。因为信号量正在被初始化,所以这时没有任何任务等待该信号量。
最后,OSSemCreate()返回给调用函数一个指向 事件 控制块的指针。以后对信号量的所有操作,如OSSemPend(), OSSemPost(), OSSemAccept()和OSSemQuery()都是通过该指针完成的。因此,这个指针实际上就是该信号量的句柄。如果系统中没有可用的 事件 控制块,OSSemCreate()将返回一个NULL指针。
创建好一个信号之后,可以调用OSSemQuery()查询一个信号的状态。该函数有两个参数:一个是指向信号量对应事件控制块的指针pevent。;另一个是指向用于记录信号量信息的数据结构OS_SEM_DATA。
简单来说就是把信号量对应的事件控制块的信息复制到数据结构OS_SEM_DATA。
OSSemQuery()程序代码如下:
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata) |
{ |
INT8U i; |
INT8U *psrc; |
INT8U *pdest; |
|
OS_ENTER_CRITICAL(); |
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1) |
OS_EXIT_CRITICAL(); |
return (OS_ERR_EVENT_TYPE); |
} |
pdata->OSEventGrp = pevent->OSEventGrp; (2) |
psrc = &pevent->OSEventTbl[0]; |
pdest = &pdata->OSEventTbl[0]; |
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { |
*pdest++ = *psrc++; |
} |
pdata->OSCnt = pevent->OSEventCnt; (3) |
OS_EXIT_CRITICAL(); |
return (OS_NO_ERR); |
} |
OS_EVENT *OSSemCreate (INT16U cnt) ;该函数返回的数据类型为指针,指针指向的数据类型为OS_EVENT(事件的数据类型为结构体)。也就是函数返回一个地址,地址里存的是新创建的结构体类型所占据的内存的首地址。
OS_EVENT *pevent;
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
;首先创建一个指向OS_EVENT结构体类型的指针pevent;系统初始化时会创建一个空事件控制块链表,而创建的空事件控制块的数目由常数OSEventMax来决定。OSEventFreeList就指向这个空事件控制块链表的第一个,所以上面的程序里把OSEventFreeList赋给了pevent,也就是让pevent指向第一个空事件控制块,然后又让OSEventFreeList指向了空事件控制链表中的第二个。也就是从空事件链表中摘出了第一个给事件使用。
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_SEM;
pevent->OSEventCnt = cnt;
pevent->OSEventPtr = (void *)0;
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?';
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
;以上就是对新创建的空任务控制块的各个量进行初始化。
OS_EventWaitListInit(pevent); ;通过调用OSEventWaitListInit()对事件控制块中的等待任务列表进行初始化。该函数初始化一个空的等待任务列表,其中没有任何任务。该函数的调用参数只有一个,就是指向需要初始化的事件控制块的指针pevent。
return (pevent); ;返回创建的结构体类型数据的首地址的指针