等待一个信号量, OSSemPend()

时间:2021-01-09 20:40:28

程序清单 L6.10是OSSemPend()函数的源代码。它首先检查指针pevent所指的任务控制块是否是由OSSemCreate()建立的[L6.10(1)]。如果信号量当前是可用的(信号量的计数值大于0)[L6.10(2)],将信号量的计数值减1[L6.10(3)],然后函数将“无错”错误代码返回给它的调用函数。显然,如果正在等待信号量,这时的输出正是我们所希望的,也是运行OSSemPend()函数最快的路径。

如果此时信号量无效(计数器的值是0),OSSemPend()函数要进一步检查它的调用函数是不是中断服务子程序[L6.10(4)]。在正常情况下,中断服务子程序是不会调用OSSemPend()函数

的。这里加入这些代码,只是为了以防万一。当然,在信号量有效的情况下,即使是中断服务子程序调用的OSSemPend(),函数也会成功返回,不会出任何错误。

如果信号量的计数值为0,而OSSemPend()函数又不是由中断服务子程序调用的,则调用OSSemPend()函数的任务要进入睡眠状态,等待另一个任务(或者中断服务子程序)发出该信号量(见下节)。OSSemPend()允许用户定义一个最长等待时间作为它的参数,这样可以避免该任务无休止地等待下去。如果该参数值是一个大于0的值,那么该任务将一直等到信号有效或者等待超时。如果该参数值为0,该任务将一直等待下去。OSSemPend()函数通过将任务控制块中的状态标志.OSTCBStat置1,把任务置于睡眠状态[L6.10(5)],等待时间也同时置入任务控制块中[L6.10(6)],该值在OSTimeTick()函数中被逐次递减。注意,OSTimeTick()函数对每个任务的任务控制块的.OSTCBDly域做递减操作(只要该域不为0)[见3.10节,时钟节拍]。真正将任务置入睡眠状态的操作在OSEventTaskWait()函数中执行 [见6.03节,让一个任务等待某个事件,OSEventTaskWait()][L6.10(7)]。

因为当前任务已经不是就绪态了,所以任务调度函数将下一个最高优先级的任务调入,准备运行[L6.10(8)]。当信号量有效或者等待时间到后,调用OSSemPend()函数的任务将再一次成为最高优先级任务。这时OSSched()函数返回。这之后,OSSemPend()要检查任务控制块中的状态标志,看该任务是否仍处于等待信号量的状态[L6.10(9)]。如果是,说明该任务还没有被OSSemPost()函数发出的信号量唤醒。事实上,该任务是因为等待超时而由TimeTick()函数把它置为就绪状态的。这种情况下,OSSemPend()函数调用OSEventTO()函数将任务从等待任务列表中删除[L6.10(10)],并返回给它的调用任务一个“超时”的错误代码。如果任务的任务控制块中的OS_STAT_SEM标志位没有置位,就认为调用OSSemPend()的任务已经得到了该信号量,将指向信号量ECB的指针从该任务的任务控制块中删除,并返回给调用函数一个“无错”的错误代码[L6.10(11)]。

 

程序清单 L6.10 等待一个信号量

void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

{

    OS_ENTER_CRITICAL();

    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {                  (1)

        OS_EXIT_CRITICAL();

        *err = OS_ERR_EVENT_TYPE;

    }

    if (pevent->OSEventCnt > 0) {                                    (2)

        pevent->OSEventCnt--;                                        (3)

        OS_EXIT_CRITICAL();

        *err = OS_NO_ERR;

    } else if (OSIntNesting > 0) {                                   (4)

        OS_EXIT_CRITICAL();

        *err = OS_ERR_PEND_ISR;

    } else {

        OSTCBCur->OSTCBStat    |= OS_STAT_SEM;                       (5)

        OSTCBCur->OSTCBDly      = timeout;                           (6)

        OSEventTaskWait(pevent);                                     (7)

        OS_EXIT_CRITICAL();

        OSSched();                                                   (8)

        OS_ENTER_CRITICAL();

        if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {                     (9)

            OSEventTO(pevent);                                      (10)

            OS_EXIT_CRITICAL();

            *err = OS_TIMEOUT;

        } else {

            OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;                (11)

            OS_EXIT_CRITICAL();

            *err = OS_NO_ERR;

        }

    }

}