信号量的操作及原理

 
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. linux的常用指令和配置文件

    一. 常用的指令 mkdir -p 创建文件夹 parents递归创建 ls -alh 查看当前目录内容 cd   切换工作目录 pwd 打印当前工作目录 touch 文件名 创建文件 echo 字符 ...

  2. 分布式文件系统 fastdfs搭建

    fastdfs第一步:在home文件夹下建立tar文件夹第二步:cd /home/tar///(Libevent 是一个用C语言编写的.轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( e ...

  3. PC装MAC(VM虚拟机)想体验苹果系统的福利

    Windows下 VM12虚拟机安装OS X 10.11(详细教程) 工具/原料 Mac OS X 10.11 镜像文件 unlocker208文件 VMware Workstation12(版本不一 ...

  4. macOS 安装 ctags

    macOS 安装 ctags macOS 自带一个 ctags,但是不支持 -R 参数,递归产生tags文件 $ ctags -R --exclude=.git --exclude=log * cta ...

  5. ubuntu installs matlab2017a

    cd mkdir matlab sudo mount -o loop *1.iso matlab sudo ./matlab/install ... sudo mount -o loop *2.iso ...

  6. 2019/3/2周末 java集合学习(一)

    Java集合学习(一) ArraysList ArraysList集合就像C++中的vector容器,它可以不考虑其容器的长度,就像一个大染缸一 样,无穷无尽的丢进去也没问题.Java的数据结构和C有 ...

  7. 看CES 2017上有哪些好玩的物联网设备

    2017年国际消费类电子产品展览会(CES)已于昨天在拉斯维加斯正式开幕,多款新一代智能手机和平板电脑亮相本届CES大展,智能家居.穿戴等设备更成为外界普遍关注的焦点.今天笔者将带大家一览CES 20 ...

  8. VSCode插件开发全攻略(五)跳转到定义、自动补全、悬停提示

    更多文章请戳VSCode插件开发全攻略系列目录导航. 跳转到定义 跳转到定义其实很简单,通过vscode.languages.registerDefinitionProvider注册一个provide ...

  9. kaldi的TIMIT实例三

    ============================================================================ MMI + SGMM2 Training &a ...

  10. FineCMS 5.0.10 多个 漏洞详细分析过程

    0x01 前言 已经一个月没有写文章了,最近发生了很多事情,水文一篇.今天的这个CMS是FineCMS,版本是5.0.10版本的几个漏洞分析,从修补漏洞前和修补后的两方面去分析. 文中的evai是特意 ...