信号量的操作及原理

 
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指针无效
 
下面讲解一下函数内部原理:
 
    如果信号量的计数器值大于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);
 

【原创】ucos信号量的操作及原理的更多相关文章

  1. 信号量的操作——semop函数

    信号量的值与相应资源的使用情况有关,当它的值大于 0 时,表示当前可用的资源数的数量:当它的值小于 0 时,其绝对值表示等待使用该资源的进程个数.信号量的值仅能由 PV 操作来改变.        在 ...

  2. Spark- Spark普通Shuffle操作的原理剖析

    在spark中,什么情况下会发生shuffle? reduceByKey,groupByKey,sortByKey,countByKey,join,cogroup等操作. 默认的shuffle操作的原 ...

  3. Stream常用操作以及原理探索

    Stream常用操作以及原理 Stream是什么? Stream是一个高级迭代器,它不是数据结构,不能存储数据.它可以用来实现内部迭代,内部迭代相比平常的外部迭代,它可以实现并行求值(高效,外部迭代要 ...

  4. ucos信号量集源码分析

    在实际的应用之中,一个任务经常需要等待多个信号量的同时生效,或者说任务需要根据多个信号量的组合作用的结果来决定任务的运行方式,为了实现这种多信号量组合的功能,ucos实现了信号量集的特殊结构. 信号量 ...

  5. RapidIO 逻辑层IO操作与Message操作的原理和区别

    接上一篇 SRIO RapidIO (SRIO)协议介绍(一) 1     说明 查看协议手册时会发现,逻辑层的操作分成了IO和Message 2类动作,那么为什么要分成2类操作?从原理和应用角度来看 ...

  6. 操作系统核心原理-7.设备管理:I/O原理

    一.I/O的基本知识 1.1 为何要有I/O 前面阐述了操作系统具有进程管理.内存管理.外存管理三大核心功能,但是计算机归根是为人类服务的,这就要求计算机必须提供某种机制使得人们可以向计算机发出命令或 ...

  7. 使用Memcache缓存mysql数据库操作的原理和缓存过程浅析

    转载自脚本之家 http://www.jb51.net/article/51831.htm  作者:忙碌的松鼠 对于大型网站如facebook,ebay等网站,如果没有Memcache做为中间缓存层, ...

  8. [原创]gerrit上分支操作记录(创建分支、删除分支)

    Git分支对于一个项目的代码管理而言,是十分重要的! 许多久用git的朋友可能已经掌握的很牢固了,但对于一些初涉git的童鞋来说,可能还不是很熟悉. 在此,我将自己的一些操作经历做一梳理,希望能帮助到 ...

  9. java信号量PV操作 解决生产者-消费者问题

    package test1; /** * 该例子演示生产者和消费者的问题(设只有一个缓存空间.一个消费者和一个生产者) * MySystem类定义了缓冲区个数以及信号量 * @author HYY * ...

随机推荐

  1. 计算机爱好者协会技术贴markdown第四期

    首先先让爱酱用CSDN自带的数学公式方法来闪瞎大家的钛合金狗眼: 有没有感觉到Markdown的强大!!!!! ## KaTeX数学公式 您可以使用渲染LaTeX数学表达式 [KaTeX](https ...

  2. 更新windows补丁时一直卡在搜索更新

    在微软下载好安装补丁Windows8.1-KB2999226-x64后,双击时一直停留在“正在此计算机上搜索”界面. 解决方案: 1.将windows 自动更新设置为:“从不检查更新”  . 2.关闭 ...

  3. Win7 VS2017编译PBR渲染引擎google filament

    按照官方说明 https://github.com/google/filament 前置工具包 Windows 10 SDKVisual Studio 2017Clang 6Python 3.7Git ...

  4. abaqus邓肯张模型umat

    首先是始点刚度法: SUBROUTINE UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD, RPL,DDSDDT,DRPLDE,DRPLDT, STRAN,DSTRAN,T ...

  5. [swarthmore cs75] Compiler 6 – Fer-de-lance

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第8次大作业. First-class function: It treats function ...

  6. Activiti工作流数据库表详细介绍

    Activiti的后台是有数据库的支持,所有的表都以ACT_开头. 第二部分是表示表的用途的两个字母标识. 用途也和服务的API对应. ACT_RE_*: 'RE'表示repository. 这个前缀 ...

  7. 闵可夫斯基和(Mincowsky sum)

    一.概述 官方定义:两个图形A,B的闵可夫斯基和C={a+b|a∈A,b∈B}通俗一点:从原点向图形A内部的每一个点做向量,将图形B沿每个向量移动,所有的最终位置的并便是闵可夫斯基和(具有交换律) 例 ...

  8. vue watch高级用法

    watch是vue组件最常用,最重要的功能之一,我们都知道它最基本的特性就是监听的data对象里的属性变化了,watch就会自动执行. 那它还有什么骚操作呢?下面我带大家看看,菜鸟一枚,如有错误欢迎指 ...

  9. JSHFJK师德师风幅度十分时尚大方JSHFJK

    sdjfhjksd{104411661166112205880477047710881111099909771088104411111155116605880533055505330500051104 ...

  10. 可参数化的带优先级的数据选择器的FPGA实现方式探讨

    在FPGA设计中,大部分情况下我们都得使用到数据选择器.并且为了设计参数化,可调,通常情况下我们需要一个参数可调的数据选择器,比如M选1,M是可调的参数. 如果,数据选择器是不带优先级的,我们可以使用 ...