zoukankan      html  css  js  c++  java
  • 【原创】ucos信号量的操作及原理

    信号量的操作及原理

     
    1.OSSemCreate创建信号量semaphore
        在使用信号量之前,要先用OSSemCreate创建一个信号量,并通过返回的合法事件结构体指针使用信号量。
    1. OS_EVENT *OSSemCreate(INT16U cnt)
    2. {
    3. #if OS_CRITICAL_METHOD == 3 /* 原理请查看http://blog.csdn.net/liuhui_8989/article/details/8783323 */
    4. OS_CPU_SR cpu_sr;
    5. #endif
    6. OS_EVENT *pevent;
    7. if(OSIntNesting>0){/* 不能在中断内创建信号量 */
    8. return((OS_EVENT *)0);/* 直接返回0 */
    9. }
    10. OS_ENTER_CRITICAL();
    11. pevent =OSEventFreeList;/* 获取空闲的事件控制块 */
    12. if(OSEventFreeList!=(OS_EVENT *)0){/* 将OSEventFreeList指向下一个事件控制块 */
    13. OSEventFreeList=(OS_EVENT *)OSEventFreeList->OSEventPtr;
    14. }
    15. OS_EXIT_CRITICAL();
    16. if(pevent !=(OS_EVENT *)0){/* Get an event control block */
    17. pevent->OSEventType= OS_EVENT_TYPE_SEM;
    18. pevent->OSEventCnt= cnt;/* 设置计数器的初值 */
    19. pevent->OSEventPtr=(void*)0;/* Unlink from ECB free list */
    20. OS_EventWaitListInit(pevent);/* 初始化事件控制块中任务等待表为0 */
    21. }
    22. return(pevent);
    23. }
        简而言之,如果没有了空闲的事件控制块或者是在中断内创建信号里,则返回无效的事件控制块0;否则返回类型为信号量,任务等待表OSEventGrp和OSEventTbl[]为0,且已设置了计数器初值的事件控制块指针。这样便成功地创建了一个信号量。这里要注意,使用OSSemCreate函数返回的指针前,要检验是否为有效的指针。
        cnt的值至少为0。
        创建了信号量之后,便可以对信号量进行如下操作了,申请、释放、删除、查询信号量。
     
    2. 信号量的申请和释放
    1. 申请:voidOSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err)
    2. 释放:INT8U OSSemPost(OS_EVENT *pevent)
        OSSemPend函数有三个参数,
        第一个是一个指向事件控制块的指针,该值为OSSemCreate返回值。
        第二个是一个等待时间值(至少为0),如果信号量目前被占用,则无法立即申请到信号量,调用该函数的任务将被挂起,如果等待时间值为0,则一直被挂起直到信号量被释放为止(OSSemPost能够在释放信号量的同时,恢复等待信号量的任务),如果等待时间值大于0,则在超时时间过后,由OSTineTick恢复为就绪状态的任务。
        第三个为一个指向错误代码的指针,该值作为函数返回值使用。
        OS_NO_ERR                       函数调用成功,获得了信号量。
        OS_TIMEOUT                     在规定的时间内没有申请到信号量
        OS_ERR_EVENT_TYPE       事件类型错误,不是信号量
        OS_ERR_PEND_ISR            不能在中断内申请信号量
        OS_ERR_PEVENT_NULL    pevent指针无效
     
    下面讲解一下函数内部原理:
    无标题.png
     
        如果信号量的计数器值大于0,则将其减1,表示又有一个任务占用,并直接返回。
        如果信号量的计数器值为0,表示信号量已被其他任务占用,此时任务控制块中的状态标志是等待信号状态以及就绪的,因为使用的是按位或操作,保留了原有的就绪状态标志。
        之后调用了OS_EventTaskWait(pevent); 此时作了3件事:
        (1)OSTCBCur->OSTCBEventPtr = pevent;将事件控制块指针保存于任务控制块中
        (2)去除任务在任务就绪表的就绪状态,注意没有包括任务控制块中的状态标志
        (3)设置事件控制块中的任务等待表
        至此任务被挂起!通过OS_Sched运行其他任务去了。
        接下来的结果取决于,信号量是否在规定的等待时间内被释放。
        在当前任务被挂起,而运行其他任务的同时,每个时钟节拍都会运行OSTimeTick中断函数,此函数会遍历所有任务,如果任务控制块中的状态标志为就绪的,且Dly等待值不为0,则将Dly减1,如果减1后刚好为0,则在任务就绪表中恢复该任务的就绪状态!
        如果该就绪状态的任务恢复运行,此时任务控制块的状态标志仍为OS_STAT_SEM,运行OS_EventTO,做的事刚好和OS_EventTaskWait相反。
        (1)OSTCBCur->OSTCBEventPtr = 0;
        (2)设置任务控制块中的状态标志为就绪状态(去除OS_STAT_SEM状态
        (3)去除事件控制块中的任务等待表
        此时返回OS_TIMEOUT。
        但是如果在等待时间未过去,其他任务释放了信号量,OSSemPost能够在释放信号量的同时,恢复等待信号量的任务。等待信号量的任务恢复运行,此时任务控制块的状态标志不包含OS_STAT_SEM了,所以函数直接跳过第二个if语句,返回OS_NO_ERR。
     
    释放信号量过程:
        函数OSSemPost在对信号量的计数器操作之前,首先检查任务等待表中是否还有其他等待该信号的任务,如果没有,就把计数器加1,如果有,则调用OS_EventTaskRdy将任务等待表中最高优先级的任务设为就绪状态,并调用OSSched调度任务。
     
    3. 应用
    3.1申请函数和释放函数在同一任务中成对出现
    main:
    pevent = OSSemCreate(1);
    task1:
    OSSemPend(pevent, 0, err);
    ....
    OSSemPost(pevent);
    task2:
    OSSemPend(pevent, 0, err);
    ....
    OSSemPost(pevent);
    当一个任务没有释放信号量,另一个任务在申请信号量时只能挂起直到信号量释放。
    3.2 应用程序中有一个函数Fun(),如果想使任务M必须经过Y任务允许才能调用函数一次,可以使用信号量
    main:
    pevent = OSSemCreate(0);
    task1:
    OSSemPend(pevent, 0, err);
    Fun();
    task2:
    OSSemPost(pevent);
     
  • 相关阅读:
    C# 让程序自动以管理员身份运行
    [转]SAP算号器 license key Developer Access Key 完美解决方案
    【原创】项目性能优化全纪录(一) 存储过程优化
    treeview的遍历
    .NET求职笔试题目(续)
    SQL server 数据同步 Merge 的一个小bug
    use Stored procedure return a tabel(存储过程返回table)
    四种sql server 数据库分页的测试
    十五个世界最顶级的技术类博客网站
    层的拖动与隐藏
  • 原文地址:https://www.cnblogs.com/cposture/p/4299055.html
Copyright © 2011-2022 走看看