在实际的应用之中,一个任务经常需要等待多个信号量的同时生效,或者说任务需要根据多个信号量的组合作用的结果来决定任务的运行方式,为了实现这种多信号量组合的功能,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两个,其余的接口会自动赋值,不需要用户也不能去手动赋值,切记

ucos信号量集源码分析的更多相关文章

  1. Semaphore(信号量)源码分析

    1. Semaphore Semaphore和ReentrantReadWriteLock.ReadLock(读锁)都采用AbstractOwnableSynchronizer共享排队的方式实现. 关 ...

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

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

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

    quartz2.2.1集群调度机制调研及源码分析 原文地址:http://demo.netfoucs.com/gklifg/article/details/27090179 引言quartz集群架构调 ...

  4. [转]RMI方式Ehcache集群的源码分析

    RMI方式Ehcache集群的源码分析   Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示:   Ehcache支持 ...

  5. RMI方式Ehcache集群的源码分析

    Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示: Ehcache支持多种集群方式,下面以RMI通信方式为例,来具体分 ...

  6. 分布式缓存技术之Redis_Redis集群连接及底层源码分析

    目录 1. Jedis 单点连接 2. Jedis 基于sentinel连接 基本使用 源码分析 本次源码分析基于: jedis-3.0.1 1. Jedis 单点连接   当是单点服务时,Java ...

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

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

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

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

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

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

随机推荐

  1. C++builder编译别人工程报错

    编译时遇到错误,信息如下: [C++ Error] NVRAMEditor.h(83): E2209 Unable to open include file 'CONTROLSLib_OCX.h'[C ...

  2. sql 日结

    --生成日结数据 ==================================== -- Author: <Author,,Name> -- Create date: <Cr ...

  3. Tomcat 虚拟目录映射

    最近老是被一个旧Ant工程所困扰,代码版本都改好了测试也通过了,就是打不了war包,一看build.xml 我的天 各种逆天啊....头大.于是乎想起了最基础的tomcat虚拟目录虽是一个很基础的点, ...

  4. ormlite 删除操作

    ormlite删除操作 DeleteBuilder<TransferDetailDtl, Integer> deleteBuilder = mRawDao.deleteBuilder(); ...

  5. Android Studio的使用(九)--设置IDE编码格式

    1.打开设置 2.勾选编码格式,在这里可以设置分别设置IDE.Project.File等级别的编码格式. 3.查看.修改各个文件的编码 4.当右击编辑界面时,可以直接设置当前文件的编码

  6. Mac linux 安装memcached服务 用法

    今天在Mac上安装memcached服务的时候 由于安装memcached之前需要安装libevent依赖包 所以使用brew install libevent 安装过程中报错 Warning: Yo ...

  7. select, poll, epoll

    select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024 http://www.cnblogs.com/bigwangdi/p/3182958.html ...

  8. angularJS 系列(六)---$emit(), $on(), $broadcast()的使用

    下面以一个例子来讲述 angular 中的event system,有$emit(), $on(), $broadcast().效果图如下 下面的代码中,用到了 controller AS 的语法,具 ...

  9. 强制设置IE浏览器的版本模式

    转载自:http://blog.csdn.net/huwenhu2007/article/details/17954119 1.<meta http-equiv="X-UA-Compa ...

  10. Struts2--Global Result全局结果集

    如果有很多action,有共同的result指向, 而且属于不同的包,那么可以继承上面的包, 然后写一个<global-results> 1. jsp显示文件: <ol> &l ...