在实际的应用之中,一个任务经常需要等待多个信号量的同时生效,或者说任务需要根据多个信号量的组合作用的结果来决定任务的运行方式,为了实现这种多信号量组合的功能,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. SuperSocket+unity 网络笔记

    学习SuperSocket 必须要注意的 代码是 static void Main(string[] args) { WebSocketServer appServer = new WebSocket ...

  2. 利用BFS求最短路

    利用BFS求图的最短路, POJ3984 #define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<string.h& ...

  3. CodeForces 687A NP-Hard Problem(二分图判定)

    这本来一个挺简单的题呢,结果让我给想复杂了,二分图就是把图分成了两部分,然后不同颜色各一边,肯定是满足题目中说的边和点的条件的,真是犯二了.. 代码如下: #include<iostream&g ...

  4. thinkPHP17---操作绑定到类

    首先要配置: "ACTION_BIND_CLASS"=>"TRUE"; 控制器类的定义如下: namespace Home\Controller\Inde ...

  5. Python 安装matplotlib,six,dateutil,pyparsing 完整过程

    [摘要:正在做词频剖析的时间,须要用matlotlib 做图表,柱状图啥的,因而便最先了一个又一个的装置库的进程 由于matplotlib 须要依附很多其他科教盘算的第三圆库,须要一个一个的装置了.. ...

  6. call_compile.sql

    set echo off prompt prompt ========================================================================= ...

  7. android脚步---APP引导页添加

    package com.leadcore.uudatoutie; import java.util.ArrayList; import com.leadcore.uudatoutie.R; impor ...

  8. 手把手教你使用startuml画用例图

    转自:http://www.2cto.com/os/201502/377091.html 最近准备研究下volley的源码,但看了网上一些大牛的博客都是配合图这样看起来更直观,分析起来逻辑也很好,什么 ...

  9. MVC和三层架构的区别

    MVC是一种设计模式,Modal,View,Controller,三层分开,解开耦合,方便替换. 三层架构包括 UI层,业务逻辑层,Dao层.UI层负责展示数据,业务逻辑层具体处理数据,Dao层负责和 ...

  10. 推荐!国外程序员整理的Java资源大全

    http://www.importnew.com/14429.html 本文由 ImportNew - 唐尤华 翻译自 github akullpp.欢迎加入翻译小组.转载请见文末要求. 构建 这里搜 ...