UC/OS II事件管理(2)之信号量管理

信号量在资源互斥共享 任务同步与通信中有着广泛的应用。信号量也是事件中的种,信号量的函数实现在ucos的os_sem.c文件中。
信号量是一个非负整数,当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。
当进程不再使用一个信号量控制的共享资源时,信号量的值+1,当进程成功请求到一个信号的时候,信号量的值就减一
关于信号量的函数,主要包括下8个函数,
1:信号创建函数OSSemCreate
信号创建函数的原型如下所示
OS_EVENT *OSSemCreate (INT16U cnt);
在系统刚开始,初始化之后,所有的事件控制块(ECB)都为空闲事件控制块。创建信号函数就是从空闲事件控制块链表中去出一个控制块,进行配置,使其具备有信号量的属性。并返回该任务控制块的指针,若返回的是空指针,则创建不成功。

其函数主体如下,前面都是一些参数检查,不再赘述:
pevent = OSEventFreeList;
if (OSEventFreeList != (OS_EVENT *)0) { //看链表是否空
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;}
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_SEM;//定义事件类型为信号量
pevent->OSEventCnt = cnt; //设置信号量的值
pevent->OSEventPtr = (void *)0; //该ECB不在任何的链表中了,所以该指针指向空地址
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?"; //任务控制块名字
#endif
OS_EventWaitListInit(pevent); //初始化时间等待表和数组。(就是将所有元素清零,因为该ECB可能之前被调用过)
}
return (pevent);

2:请求信号函数OSSemPend
信号请求函数原型为:void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr);
该函数的三个参数为对应信号的ECB指针OS_EVENT *pevent ,等待超时时间timeout,返回结果放在指向整数的整形指针。
该函数的主要功能是请求信号量,当信号量大于可用的时候,成功返回,当信号量不大于0不可用的时候,任务会阻塞,直到有信号量可用或者是时间超时了为止,然后返回状态。
函数的前面都是一些常见的参数检查,其功能主体的部分代码如下:
if (pevent->OSEventCnt > 0u) {                    //信号量的值大于0的时候,说明资源可用
        pevent->OSEventCnt--;                         //将信号量减1
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;          //申请成功,返回
        return;
    }
                                                      //下面是若是信号量的值不大于0
    OSTCBCur->OSTCBStat     |= OS_STAT_SEM;           // 将申请信号量任务的任务控制块成员OSTCBStat添加等待信号量状态
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK; //任务控制块事件等待标志置位
    OSTCBCur->OSTCBDly       = timeout;               //设置超时时间
    OS_EventTaskWait(pevent);                         //设置事件等待,任务在ECB事件表和数组中登记,任务阻塞
    OS_EXIT_CRITICAL();
    OS_Sched();                                       //执行调度,运行其他任务
    OS_ENTER_CRITICAL();
   
    switch (OSTCBCur->OSTCBStatPend) {                //当任务不在阻塞再次执行 ,检查任务控制块事件等待状态
        case OS_STAT_PEND_OK:     //等待事件发生了,解除阻塞状态
             *perr = OS_ERR_NONE;
             break;

        case OS_STAT_PEND_ABORT:
             *perr = OS_ERR_PEND_ABORT;               //因为异常,解除阻塞状态
             break;

        case OS_STAT_PEND_TO:   //超时
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);   //取消等待事件,更新ECB的事件表和事件组
             *perr = OS_ERR_TIMEOUT;                  //超时
             break;
    }
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      //更新任务状态
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  //更新等待事件状态
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;    //事件控制块指针清零

3:提交信号量OSSemPost
当任务在对资源完成访问之后,应该讲信号量进行加1,表示资源释放,同时若是有其他任务在等待该信号量,则应该将等待任务唤醒。
提交信号量函数就是实现这个功能。
其函数的原型为:INT8U OSSemPost(OS_EVENT *pevent);参数为信号量的事件控制块指针
除了前面的参数检查之外,函数的主题功能代码如下:
OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) {                   //查看该是否有任务等待该信号量
                                                     
        (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK); //因为有有一个信号量释放,所以可以将该等待该信号量中的一个任务唤醒,运行状态转为就绪
        OS_EXIT_CRITICAL();
        OS_Sched();                                   //执行调度,运行最高优先级任务
        return (OS_ERR_NONE);               //程序返回
    }
    if (pevent->OSEventCnt < 65535u) {                //若是没有其他任务等待该信号量,若是有,执行前面的if语句并返回了
        pevent->OSEventCnt++;                         //信号量计数加1
        OS_EXIT_CRITICAL();
        return (OS_ERR_NONE);
    }
    OS_EXIT_CRITICAL();     
4: 查询信号量OSSemQuery
函数的原型如下:INT8U OSSemQuery (OS_EVENT *pevent,OS_SEM_DATA *p_sem_data);
该函数查询信号量的信息,并且将信息存入到p_sem_data指针指向的OS_SEM_DATA的结构体内。
其主要功能代码实现如下:
OS_ENTER_CRITICAL();
    p_sem_data->OSEventGrp = pevent->OSEventGrp;           //等待信号量表
    psrc                   = &pevent->OSEventTbl[0];     
    pdest                  = &p_sem_data->OSEventTbl[0];     
    for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) {
        *pdest++ = *psrc++;          //等待信号量数组
    }
    p_sem_data->OSCnt = pevent->OSEventCnt;                //信号量计数
    OS_EXIT_CRITICAL();

5:无等待请求信号量OSSemAccept
该函数的功能顾名思义知道,请求信号量,若是信号量可用,就使用信号量。若是没有,不阻塞,继续执行其他代码
其原型为:INT16U OSSemAccept (OS_EVENT *pevent);根据函数返回的结果时候大于或等于0,判断是否申请成功。
其功能代码实现如下:
OS_ENTER_CRITICAL();
    cnt = pevent->OSEventCnt;
    if (cnt > 0u) {                                   
        pevent->OSEventCnt--;                       
    }
    OS_EXIT_CRITICAL();

6:放弃等待信号量OSSemPendAbort
放弃等待信号量,需要注意的是,肯定不会是请求信号量的任务请求放弃,因为请求信号量的任务在阻塞状态中,根本得不得执行。根本不可能自己去放弃等待,要放弃等待也是其他任务来执行。
其函数原型如下:INT8U  OSSemPendAbort (OS_EVENT  *pevent, INT8U      opt, INT8U     *perr) 
参数opt有两种处理情况,OS_PEND_OPT_NONE和OS_PEND_OPT_BROADCAST
OS_PEND_OPT_NONE            将等待该信号量的最高优先级任务就绪,其他不变
OS_PEND_OPT_BROADCAST   将等待该信号量的所有任务就绪
其功能代码实现如下:
OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) {                   //是否有等待该信号量的任务
        nbr_tasks = 0u;
        switch (opt) {
            case OS_PEND_OPT_BROADCAST:               //若是OS_PEND_OPT_BROADCAST情况
                 while (pevent->OSEventGrp != 0u) {   //将所有等待信号的任务的运行状态转为就绪状态
                     (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);
                     nbr_tasks++;
                 }
                 break;

            case OS_PEND_OPT_NONE: //若是OS_PEND_OPT_NONE
            default:                                  
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT); //将等待信号的最高优先级任务转为就绪状态
                 nbr_tasks++;
                 break;
        }
        OS_EXIT_CRITICAL();
        OS_Sched();                                   //执行调度,运行最高优先级就绪任务
        *perr = OS_ERR_PEND_ABORT;
        return (nbr_tasks);
    }
    OS_EXIT_CRITICAL();
7:信号量值得设置OSSemSet
该函数的主要功能就是重新设置信号量计数的值,在一般情况下,不需要使用该函数,因为在创建该函数的时候,已经对信号量的计数值进行过赋值。
该函数非常简单,只需要修改ECB的OSEventCnt的值。
函数原型为:void OSSemSet(OS_EVENT *pevent,INT16U cnt,INT8U *perr);其中pevent为事件控制块指针,cnt为需要设置的信号量计数值,返回结果放在指向整数的整形指针perr。
其主体功能代码如下:
OS_ENTER_CRITICAL();
    *perr = OS_ERR_NONE;
    if (pevent->OSEventCnt > 0u) {              //信号量计数值是否大于0     
        pevent->OSEventCnt = cnt;                  
    } else {                                        
        if (pevent->OSEventGrp == 0u) {               //该信号量是否有任务等待?有的话修改不成功
            pevent->OSEventCnt = cnt;                
        } else {
            *perr              = OS_ERR_TASK_WAITING;
        }
    }
    OS_EXIT_CRITICAL();

8:删除信号量OSSemDel
如果信号量不用就尽快删除,否则一直占着。而ECB控制块数量有限(默认为十个啊),很有可能不够用。
其函数原型如下:OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr); 
opt有两种情况OS_DEL_NO_PEND和OS_DEL_ALWAYS
OS_DEL_NO_PEND  若是没有等待信号量的任务,则删除该任务
OS_DEL_ALWAYS    不管有没有任务等待,始终删除
其主要代码如下:
if (pevent->OSEventGrp != 0u) {                        //是否有任务等待
        tasks_waiting = OS_TRUE;                           //有的话tasks_waiting = OS_TRUE
    } else {
        tasks_waiting = OS_FALSE;                          //没有
    }
    switch (opt) {
        case OS_DEL_NO_PEND:                               //opt为OS_DEL_NO_PEND的情况
             if (tasks_waiting == OS_FALSE) {    //没有任务等待,则下面执行信号量的删除
#if OS_EVENT_NAME_EN > 0u
                 pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
                 pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;//事件类型为未使用 
                 pevent->OSEventPtr     = OSEventFreeList; //事件控制块加入到空闲事件控制链表中
                 pevent->OSEventCnt     = 0u;
                 OSEventFreeList        = pevent;          
                 OS_EXIT_CRITICAL();
                 *perr                  = OS_ERR_NONE;  
                 pevent_return          = (OS_EVENT *)0;
             } else {  //若是有任务等待信号量的话,删除不成功
                 OS_EXIT_CRITICAL();
                 *perr                  = OS_ERR_TASK_WAITING;
                 pevent_return          = pevent;
             }
             break;

        case OS_DEL_ALWAYS:                                //当opt为OS_DEL_ALWAYS
             while (pevent->OSEventGrp != 0u) {            //逐个将等待信号的任务运行状态转为就绪态
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
             }
#if OS_EVENT_NAME_EN > 0u    //下面的删除信号量的事件控制块跟上面一样
             pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
             pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;
             pevent->OSEventPtr     = OSEventFreeList;     /* Return Event Control Block to free list  */
             pevent->OSEventCnt     = 0u;
             OSEventFreeList        = pevent;              /* Get next free event control block        */
             OS_EXIT_CRITICAL();
             if (tasks_waiting == OS_TRUE) {               /* Reschedule only if task(s) were waiting  */
                 OS_Sched();                               /* Find highest priority task ready to run  */
             }
             *perr                  = OS_ERR_NONE;
             pevent_return          = (OS_EVENT *)0;       /* Semaphore has been deleted               */
             break;

        default:
             OS_EXIT_CRITICAL();
             *perr                  = OS_ERR_INVALID_OPT;
             pevent_return          = pevent;
             break;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值