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

信号量集的基础仍然是信号量,它如同一个多个信号量组成的与非门来构成逻辑结果控制任务的执行.

信号量在ucos的实现分为两个部分,第一部分叫做标志组,其中存放了信号量集中的所有信号,第二个叫做等待任务链表,链表中的每个节点对应一个正在等待信号量集的等待任务,信号量集根据这个链表来管理等待任务

不同于消息队列消息邮箱等事件,ucos定义信号量集的时候使用了一种新的结构叫做标志组,其结构如下

typedef struct os_flag_grp {                /* 事件标志组*/

INT8U         OSFlagType;               /* 信号量集标志,必须为OS_EVENT_TYPE_FLAG                     */

void         *OSFlagWaitList;           /* 指向任务等待组第一个节点的指针 */

OS_FLAGS      OSFlagFlags;              /*信号量集的长度*/

#if OS_FLAG_NAME_EN > 0u

INT8U        *OSFlagName;

#endif

} OS_FLAG_GRP;

前几个都有注释,注意OSFlagFlags,这是一个OS_FLAGS类型的变量,在OSFlagFlags中,一个位代表一个信号量,一个标志组能容纳多少信号量取决于OS_FLAGS的长度,而OS_FLAGS的长度定义可以使8位 16位 32位的,如下

#if OS_FLAGS_NBITS == 8u

typedef  INT8U    OS_FLAGS;

#endif

#if OS_FLAGS_NBITS == 16u

typedef  INT16U   OS_FLAGS;

#endif

#if OS_FLAGS_NBITS == 32u

typedef  INT32U   OS_FLAGS;

#endif

决定其长度的宏在os_cfg.h文件中指定,一般指定为16位,根据需要决定

OSFlagWaitList指向的是等待这个信号量集的任务链表的指针,一个信号量集创建之后并没有直接和任务连接上,这点之后再说,我们先看看系统如何管理信号量集

其实信号量集的管理和之前tcb ecb以及qcb的管理类似,都是空闲链表的形式,在系统中有着一个标志组数组,如下

OS_EXT  OS_FLAG_GRP       OSFlagTbl[OS_MAX_FLAGS];

该变量指明系统最多包含的标志组的大小, OS_MAX_FLAGS是在os_cfg.h文件中定义的宏,用户根据需要进行配置

在标志组初始化的时候可以看到如下代码

for (ix = 0u; ix < (OS_MAX_FLAGS - 1u); ix++) {                 /* Init. list of free EVENT FLAGS  */

ix_next = ix + 1u;

pgrp1 = &OSFlagTbl[ix];

pgrp2 = &OSFlagTbl[ix_next];

pgrp1->OSFlagType     = OS_EVENT_TYPE_UNUSED;

pgrp1->OSFlagWaitList = (void *)pgrp2;

#if OS_FLAG_NAME_EN > 0u

pgrp1->OSFlagName     = (INT8U *)(void *)"?";               /* Unknown name                    */

#endif

}

pgrp1                 = &OSFlagTbl[ix];

pgrp1->OSFlagType     = OS_EVENT_TYPE_UNUSED;

pgrp1->OSFlagWaitList = (void *)0;

#if OS_FLAG_NAME_EN > 0u

pgrp1->OSFlagName     = (INT8U *)(void *)"?";                   /* Unknown name                    */

#endif

OSFlagFreeList        = &OSFlagTbl[0];

可以很明显的看出来,信号量集的管理类似于tcbqcb等,都是使用链表的方式管理的,在初始化的时候用一个OSFlagFreeList指向一个标志组作为空闲标志组的表头, OSFlagWaitList在没有创建标志组的时候作为链表连接各个标志组变量

OSFlagWaitList的定义如下

OS_EXT  OS_FLAG_GRP      *OSFlagFreeList;

这是标志组的组织,那么如何让标志组与信号量集产生关联,依靠的函数为OS_FlagBlock,原型如下

static  void  OS_FlagBlock (OS_FLAG_GRP  *pgrp,

OS_FLAG_NODE *pnode,

OS_FLAGS      flags,

INT8U         wait_type,

INT32U        timeout)

第一个参数是我们生成的标志组,第二个为一个pnode变量, flags为指定等待的信号量集数据,第四个为等待类型,最后为等待超时事件

挑简单的先说,等待时间不用赘述,前面已经仔细分析过,说说等待类型,标志组的等待类型分为八种,分别如下

#define  OS_FLAG_WAIT_CLR_ALL           0u  //信号全部有效才能继续执行任务

#define  OS_FLAG_WAIT_CLR_AND           0u   //但是信号有效的标识是0有效

#define  OS_FLAG_WAIT_CLR_ANY           1u  //信号有一个或者一个以上有效

#define  OS_FLAG_WAIT_CLR_OR            1u //但是信号有效的标识是0有效

#define  OS_FLAG_WAIT_SET_ALL           2u //信号全部有效才能继续执行任务

#define  OS_FLAG_WAIT_SET_AND           2u //信号有效的标识是1有效

#define  OS_FLAG_WAIT_SET_ANY           3u  //信号有一个或者一个以上有效

#define  OS_FLAG_WAIT_SET_OR            3u  //信号有效的标识是1有效

举个例子,如果此时OSFlagFlags为0b00000000,此时设置为OS_FLAG_WAIT_CLR_ALL的任务就有效,而设置为OS_FLAG_WAIT_SET_ALL的信号是无效的,因为信号位为0的时候有效,全部为0,所以有效, 信号位为1的时候有效,没有一个位是1,所以无效.

还有一个OS_FLAG_NODE元素,该元素的定义如下

typedef struct os_flag_node {

void         *OSFlagNodeNext;           /*指向下一个node节点 */

void         *OSFlagNodePrev;           /* 指向上一个node节点*/

void         *OSFlagNodeTCB;            /*指向当前任务节点的tcb*/

void         *OSFlagNodeFlagGrp;

OS_FLAGS      OSFlagNodeFlags;

INT8U         OSFlagNodeWaitType;       /* 当前等待类型             */

} OS_FLAG_NODE;

成员OSFlagNodeFlagGrp是一个反向指向信号量集标志组的指针,在等待任务链表中删除一个成员的时候或者添加成员的时候需要用到

OSFlagNodeFlags相当于一个过滤器,能够从标志组中将请求任务所需的信号量筛选出来,其余的无关信号屏蔽掉,

建立节点与标志组连接关系的接口为OS_FlagBlock,核心代码如下

OSTCBCur->OSTCBStat      |= OS_STAT_FLAG;

OSTCBCur->OSTCBStatPend   = OS_STAT_PEND_OK;

OSTCBCur->OSTCBDly        = timeout;              /* Store timeout in task's TCB                   */

#if OS_TASK_DEL_EN > 0u

OSTCBCur->OSTCBFlagNode   = pnode;                /* TCB to link to node                           */

#endif

pnode->OSFlagNodeFlags    = flags;                /* Save the flags that we need to wait for       */

pnode->OSFlagNodeWaitType = wait_type;            /* Save the type of wait we are doing            */

pnode->OSFlagNodeTCB      = (void *)OSTCBCur;     /* Link to task's TCB                            */

pnode->OSFlagNodeNext     = pgrp->OSFlagWaitList; /* Add node at beginning of event flag wait list */

pnode->OSFlagNodePrev     = (void *)0;

pnode->OSFlagNodeFlagGrp  = (void *)pgrp;         /* Link to Event Flag Group                      */

pnode_next                = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;

if (pnode_next != (void *)0) {                    /* Is this the first NODE to insert?             */

pnode_next->OSFlagNodePrev = pnode;           /* No, link in doubly linked list                */

}

pgrp->OSFlagWaitList = (void *)pnode;

y            =  OSTCBCur->OSTCBY;                 /* Suspend current task until flag(s) received   */

OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;

if (OSRdyTbl[y] == 0x00u) {

OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;

可以看到,他会先将任务挂起,然后

pnode->OSFlagNodeNext     = pgrp->OSFlagWaitList;

将任务放入等待任务列表中,最后将等待任务列表的头切换成我们刚才插入的节点,如下

pgrp->OSFlagWaitList = (void *)pnode;

这样,任务就和信号量集联系在了一起,在调度的时候我们可以说应该是在postxxx里面调度的,查看之后发现如下代码

sched = OS_FALSE;                                /* Indicate that we don't need rescheduling       */

pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;

while (pnode != (OS_FLAG_NODE *)0) {

.........

pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;

}

if (sched == OS_TRUE) {

OS_Sched();

}

也就是说,在别的任务发送信号量集的时候,该信号量集内部会发生一次等待列表的遍历操作,选择出合适的可以调度的任务,然后调用OS_Sched进行调度,从而实现信号量集

说到这里,基本上信号量集的原理上的东西就差不多了,现在说说使用

创建信号量集

OSFlagCreate,返回创建的信号量集的指针

请求信号量集

OSFlagPend

向信号量集发送信号

OSFlagPost

查询信号量集的状态

OSFlagQuery

删除信号量集

OSFlagDel

向信号量集中添加任务(这个很重要)

OS_FlagBlock,这个函数有一个OS_FLAG_NODE的参数,在生成这个变量的时候,只需要赋值waittype和flags两个,其余的接口会自动赋值,不需要用户也不能去手动赋值,切记

转载于:https://www.cnblogs.com/dengxiaojun/p/4324915.html

ucos信号量集源码分析相关推荐

  1. Golang|区块链UTXO集源码分析

    区块链UTXO集源码分析 资源 go实现区块链 前提 在未实现UTXO集之前,假设系统需要查询某个钱包地址的余额,系统需要遍历区块链的所有区块,当区块链非常长时,这种做法的成本太高了. UTXO集是未 ...

  2. ucos 初始化OSTaskCreate() 源码分析 2

    2019独角兽企业重金招聘Python工程师标准>>> //系统任务创建 OSTaskCreate 任务创建 INT8U OSTaskCreate (void (*task)(voi ...

  3. quartz集群调度机制调研及源码分析---转载

    quartz2.2.1集群调度机制调研及源码分析 引言 quartz集群架构 调度器实例化 调度过程 触发器的获取 触发trigger: Job执行过程: 总结: 附: 引言 quratz是目前最为成 ...

  4. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  5. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  6. Dubbo 源码分析 - 集群容错之 Router

    1. 简介 上一篇文章分析了集群容错的第一部分 – 服务目录 Directory.服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由.上一篇文章关于服务路由相关逻辑没有细 ...

  7. Dubbo 源码分析 - 集群容错之Directory

    1. 简介 前面文章分析了服务的导出与引用过程,从本篇文章开始,我将开始分析 Dubbo 集群容错方面的源码.这部分源码包含四个部分,分别是服务目录 Directory.服务路由 Router.集群 ...

  8. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  9. Java并发编程笔记之Semaphore信号量源码分析

    JUC 中 Semaphore 的使用与原理分析,Semaphore 也是 Java 中的一个同步器,与 CountDownLatch 和 CycleBarrier 不同在于它内部的计数器是递增的,那 ...

  10. 鸿蒙轻内核源码分析:掌握信号量使用差异

    摘要:本文带领大家一起剖析鸿蒙轻内核的信号量模块的源代码,包含信号量的结构体.信号量池初始化.信号量创建删除.申请释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十一 信号量Semap ...

最新文章

  1. XenServer安全重启xapi的方法
  2. LiberOJ #6210. 「美团 CodeM 决赛」tree 树形DP
  3. 卸载angular版本
  4. Linux下C语言程序的内存布局(内存模型)
  5. c++ string 另类写法
  6. html语言可以写模版继承吗,16-Django的模板语言(变量,标签,过滤器,继承,html转义)...
  7. ITK:将两个图像与棋盘格图案组合在一起
  8. 第六届蓝桥杯JavaC组_垒骰子_详解
  9. React Fiber源码逐个击破系列-scheduler
  10. SparkContext解析
  11. stl中copy()函数_std :: copy()函数以及C ++ STL中的示例
  12. git 无法 push 远程仓库 【Note about fast-forwards】
  13. Activemq MQTT 简单消息推送示例
  14. Linux 学习记录
  15. Power BI数据网关
  16. Appium自动化测试基础--补充:C/S架构和B/S架构说明
  17. mqtt 3.1 php代码,MQTT V3.1
  18. wifi模块服务器项目心得,一次关于WiFi 驱动移植的总结复盘
  19. 大数据如何助力“驯服”火灾?
  20. PTA求100以内的素数

热门文章

  1. 进入windows自启文件夹
  2. 你必须知道:localStorage、sessionStorage 和 Cookie 区别在什么地方
  3. 教你在Windows轻松修改Hosts文件
  4. xml+flash图片展示
  5. 二叉树中获取从根节点到某个节点的路径
  6. Eureka、Zookeeper、Consul异同点
  7. java 日历类_java常用的类---日历类
  8. 如何使用nacos配置中心统一管理配置
  9. 你需要来自trustedinstaller的权限才能删除_一文详解To B权限设计
  10. flash代码_Flash如何处理常见的代码错误(AS3)(2)