精华内容
参与话题
问答
  • VxWorks学习篇-二进制信号量

    千次阅读 2016-10-30 22:03:47
    目前国内雷达信号处理平台上所用的操作系统大多为VxWorks,在VxWorks中,信号量是一种在不同任务间有效传递消息的机制,其中包括二进制信号量、计数型信号量、互斥型信号量。在这些信号量中,二进制信号量的使用最为...

    目前国内雷达信号处理平台上所用的操作系统大多为VxWorks,在VxWorks中,信号量是一种在不同任务间有效传递消息的机制,其中包括二进制信号量、计数型信号量、互斥型信号量。在这些信号量中,二进制信号量的使用最为普遍,下面是对该类型信号量原理的分析。

    1、基本函数

    在二进制信号量中,主要涉及到下面三个函数

    semBCreate该函数作用是创建二进制信号量

    SEM_IDsemTest=semBCreate(SEM_Q_PRIORITY,SEM_FULL)

    第一个参数为SEM_Q_PRIORITY(任务基于优先级排序),或者是SEM_Q_FIFO(任务基于先进先出排序);

    第二个参数为SEM_FULL(信号量创建时即为满-可用),或者是SEM_EMPTY(创建时为空,不可用)

    semGive 该函数作用是释放信号量

    semGive(semTest)

    其实semGive并不是直接将信号量从empty状态改变为full状态,其首先要从等待该信号量的任务阻塞队列里面取出第一个任务让其执行,如果没有任务才将信号量状态改变。

    semTake 该函数的作用是等待信号量

    semTake(semTest,WAIT_FOREVER)

    其实semTake也不是直接将信号量从full转换为empty,而是将任务放入等待该信号量的任务阻塞队列。

    2、过程分析

    semBCreate创建信号量基本过程

    //底层调用semInit
    {
    	semQInit//创建等待信号量的阻塞队列
    	semaphore status//设置信号量状态
    } 
    


    semGive释放信号量基本过程

    //二进制信号量底层调用semBGive
    {
    pNode = Q_FIRST (&semId->qHead) //检测该信号量阻塞队列
    if(Q is empty)
    {
    vxAtomicSet_inline ((atomic_t *) (void *)&semId->semOwner,(atomicVal_t) SEMB_FULL_STATE);//修改信号量状态
    }
    else
    {
    	windPendQGet (&semId->qHead);//将阻塞队列中第一个任务取出,让其执行
    }
    }
    

    semTake获取信号量基本过程

    //二进制信号量底层调用semBTake
    {
    abc:	    prevVal = vxAtomicSet_inline ((atomic_t *) (void *) &semId->semOwner,SEMB_PENDED_TASK | SEMB_EMPTY_STATE);//判断信号量状态
    if(prevVal == (atomicVal_t) SEMB_FULL_STATE)//如果信号量状态为full,直接返回ok
    {
    	return (OK);
    }
    Else
    {
    windPendQPut (&semId->qHead, timeout)//将任务加入信号量阻塞队列
    if(status = KERNEL_UNLOCK ())//进入阻塞状态,如果设定有超时到时间后跳出循环,否则继续执行
    goto abc.
    
    }
    }
    

    信号量处理过程中最重要的是维护一个阻塞队列,操作该队列函数如下:

    void windPendQGet(Q_HEAD *pQHead)      /* pend queue to get first task off */
    {
        FAST WIND_TCB *pTcb = NODE_PTR_TO_TCB (Q_GET (pQHead));//从等待该信号量的队列中取出第一个任务
        pTcb->status &= ~WIND_PEND; //将任务状态清楚阻塞标志位
        if (pTcb->status & WIND_DELAY)	//判断是否有超时任务,如果有直接将任务从超时队列中移除
    	{
    		pTcb->status &= ~WIND_DELAY;
    		Q_REMOVE (&tickQHead, &pTcb->tickNode);	/* remove from queue */
    	}
        if (pTcb->status == WIND_READY)		//判断此时任务状态,如果为ready将任务放入Ready队列中
    		READY_Q_PUT (pTcb);
    }
    
    STATUS windPendQPut(
        FAST Q_HEAD *pQHead, //等待该信号量的阻塞队列
        FAST int    timeout )//判断该任务是否超时
        {
        FAST WIND_TCB *pTcbCurrent = _WRS_KERNEL_GLOBAL_ACCESS (taskIdCurrent);
        READY_Q_REMOVE (pTcbCurrent);//将任务从ready队列中移除
        pTcbCurrent->status |= WIND_PEND;//将任务状态加入pend阻塞标志位
        pTcbCurrent->pPendQ = pQHead;			/* pQHead pended on */
        Q_PUT (pQHead, PENDQ_NODE_PTR (pTcbCurrent), pTcbCurrent->priority);//将任务放入等待该信号量的阻塞队列中
        if (timeout != WAIT_FOREVER)	//判断是否有设定超时机制,默认一般不设置
    	{
    	if ((UINT64)(vxAbsTicks + timeout) < vxAbsTicks)  //计算等待时间
    	    {
    	    windCalibrateTickQ ();                        /* reset delays */
    	    vxAbsTicksZero ();
    	    }
    	/*
    	 * Set the finish time of the delay for the task.  This is used to
    	 * facilitate recalculation of timeouts on RESTART.
    	 */
    	TASK_CPUTIME_INFO_SET (pTcbCurrent, 0, (vxAbsTicks + timeout));
    	Q_PUT (&tickQHead, &pTcbCurrent->tickNode, timeout);//放入超时队列中
    	pTcbCurrent->status |= WIND_DELAY;//将任务状态标志位增加delay标示
    	}
        return (OK);
    }
    


    以上两个函数中,Q_GET,Q_PUT,Q_REMOVE,READY_Q_PUT等均为宏定义函数,作用是操作相关队列。



    展开全文
  •  二进制信号量,一个任务申请成功后,可以由另一个任务释放。   二进制信号量实现任务互斥:  打印机资源只有一个,abc三个任务共享,当a取得使用权后,为了防止其他任务错误地释放了信号量(),必须将...
    
    

    互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请

     

           二进制信号量,一个任务申请成功后,可以由另一个任务释放。

     

    二进制信号量实现任务互斥:

         打印机资源只有一个,abc三个任务共享,当a取得使用权后,为了防止其他任务错误地释放了信号量(),必须将打印机房的门关起来(进入临界段),用完后,释放信号量,再把门打开(出临界段),其他任务再进去打印。(而互斥型信号量由于必须由取得信号量的那个任务释放,故不会出现其他任务错误地释放了信号量的情况出现,故不需要有临界段。互斥型信号量是二进制信号量的子集。)

     

    二进制信号量实现任务同步:

         a任务一直等待信号量,b任务定时释放信号量,完成同步功能

     

    理解互斥量和信号量 作者: JuKevin

    互斥量(Mutex)

           互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。

           Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。Mutex可以被抽象为四个操作: - 创建 Create

     

    - 加锁 Lock

     

    - 解锁 Unlock

     

    - 销毁 Destroy

     

    Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。

     

    不同操作系统中提供的Mutex函数: 动作/系统

    Win32

    Linyx

    Solaris

     

    创建

    CreateMutex

    pthread_mutex_init

    mutex_init

     

    加锁

    WaitForSingleObject

    pthread_mutex_lock

    mutex_lock

     

    解锁

    ReleaseMutex

    pthread_mutex_unlock

    mutex_unlock

     

    销毁

    CloseHandle

    pthread_mutex_destroy

    mutex_destroy

     

     

    信号量

     

    信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

     

    信号量可以分为几类:

     

    二进制信号量(binary semaphore):

           只允许信号量取0或1值,其同时只能被一个线程获取。

     

    整型信号量(integer semaphore)

           信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。

     

    记录型信号量(record semaphore)

           每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。

     

           信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。

    计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。

     

     

    Semaphore可以被抽象为五个操作:

    - 创建 Create

     

    - 等待 Wait:

     

    线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。

     

    -释放 Post

     

    执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。

     

    -试图等待 TryWait

     

    如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。

     

    -销毁 Destroy

     

    信号量,是可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。 动作/系统

    Win32

    POSIX

     

    创建

    CreateSemaphore

    sem_init

     

    等待

    WaitForSingleObject

    sem _wait

     

    释放

    ReleaseMutex

    sem _post

     

    试图等待

    WaitForSingleObject

    sem _trywait

     

    销毁

    CloseHandle

    sem_destroy

     

     

     

    互斥量和信号量的区别

     

    1. 互斥量用于线程的互斥,信号量用于线程的同步。 ——

           这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

    2.互斥量无法保证线程对资源的有序访问,信号量可以。

     

    互斥

           是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。(cute:好比一个别墅,同时只能卖个一个人。这个人可以在门上加上任意多的锁(申请多次),但是锁必须由这个人打开,因为只有他掌握着钥匙。如果别的人真的想控制这个别墅的大门,则它首先应该把这个别墅买下来(别墅的主人放弃所有权))

     

    同步

           是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源 (cute:相当于图书馆阅览室的书卡,你申请的时候-1,另一个人还的时候则可以+1,我们都可以修改书卡的当前可用数目,这个权利是不独属于任何人的。)

     

    3. 互斥量值只能为0/1,信号量值可以为非负整数。

     

           也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

     

    4. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到

     

    ====

    http://topic.csdn.net/u/20090317/02/51AAB1DC-4DF4-4B6B-A0CB-A46D2602074F.html

    展开全文
  • ...  第7节 二进制信号量 某些资源在同一时刻只可以被一个任务操作,实时操作系统的任务抢占特性会导致这些资源可能被多个任务同时操作,从而产生...本节将讲述二进制信号量的原理,可以利用二进制信号量保护这些资源


    目前更新到5.3节,请在
    http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档

    本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载

     

    第7节 二进制信号量

    某些资源在同一时刻只可以被一个任务操作,实时操作系统的任务抢占特性会导致这些资源可能被多个任务同时操作,从而产生错误。本节将讲述二进制信号量的原理,可以利用二进制信号量保护这些资源,使多个任务只能串行的操作这些资源。

     

    有时候我们可以设计一块共享内存,用来在多个任务间传递数据,比如使用任务1向共享内存中写入数据,使用任务2从这片内存中读取数据,这样就可以实现任务1向任务2传递数据的功能,但这样做有一个问题,如果任务1正在向共享内存中写数据的过程中发生了任务切换,切换到了任务2,那么任务2所读取的共享内存中的数据就不完全是最新写入的有效数据,这样任务2就会读取到错误的数据。

    为了防止这个问题发生,最简单的办法就是使用一个全局变量来指示共享内存的访问权限,当全局变量为1时,共享内存可以被访问,当全局变量为0时共享内存不可被访问。当一个任务操作共享内存时,首先判断全局变量,如果为0,说明共享内存正在被其它任务操作,此时无法被访问,如果为1的话说明共享内存可以被访问,那么该任务则将全局变量置为0,表明共享内存已经被访问,其它任务此时不可访问共享内存。当任务操作完共享内存后将全局变量置为1,释放对共享内存的访问权限,此后共享内存又可以被再次访问。

    这个过程使用伪码描述如下:

    00001  锁中断;

    00002  

    00003  

    00004  if(1 == 全局变量)

    00005  {

    00006      

    00007      全局变量 = 0;

    00008  

    00009      解锁中断;

    00010  }

    00011  else

    00012  {

    00013      解锁中断;

    00014  

    00015      

    00016      return;

    00017  }

    00018  

    00019  对共享内存的操作;

    00020  

    00021  

    00022  全局变量 = 1;

    上述函数在运行时可能会发生重入现象,因此4~7行需要用锁中断的方式将全局变量的操作过程保护起来,虽然在22行也存在重入的问题,但22行只有一条指令涉及到对全局变量的操作,而不是一个过程,因此无需锁中断保护。这里所说的一条指令,不是指C语言的一条指令,而是汇编语言的一条指令。

     

    二进制信号量就是基于上述原理实现的,简单来说,二进制信号量就是一个全局变量,用来实现各种资源的互斥,但使用全局变量作为资源互斥的开关存在一个缺点:当任务获取不到访问权限时,它可能需要等待该权限,需要暂时放弃CPU资源,让给其它任务去运行,这就需要发生任务调度,但直接触发任务调度的软中断调度函数被封装到了操作系统内部,用户不可见,因此获取不到权限的任务也就无法主动发生任务调度切换到其它任务。

    二进制信号量将任务调度函数封装到了其内部,当任务获取不到权限被阻塞时可以直接调用软中断函数MDS_TaskSwiSched触发任务调度函数,切换到其它任务继续运行,因此,可以说二进制信号量就是全局变量+任务调度的结合体。

     

    在这节,我们将引入任务的另一个状态,阻塞态(pend),当任务获取不到信号量资源时就会进入pend态,pend态与delay态非常相似,delay态是由任务主动释放CPU资源而进入的等待状态,而pend态则是由于任务获取不到某些非CPU资源而被动进入的等待状态。如果处于pend态的任务不是永久pend状态,那么该任务也将被挂入delay表中,与处于非永久delay状态的任务一起参与tick中断的调度。当pend的时间耗尽时,任务将会被从delay表拆除,结束pend状态,重新挂入ready表,参与任务调度。

     

    在使用信号量前,需要使用MDS_SemCreate函数创建信号量,新创建的信号量可以为“空”状态或者“满”状态,为与后续章节增加的信号量保持兼容,我们将“空”定义为0,将“满”定义为0xFFFFFFFF,而不是1。任务需要使用MDS_SemTake函数获取信号量,获取信号量的过程就是信号量从满到空的过程。任务需要使用MDS_SemGive函数释放信号量,释放信号量的过程就是信号量从空到满的过程。信号量空满状态对信号量操作的对应关系如表9所示:

    操作方式

    操作后状态

    发生的结果

    MDS_SemCreate

    信号量被初始化为满状态。

    MDS_SemTake

    任务获取到信号量。

    MDS_SemTake

    任务没有获取到信号量,被阻塞。

    MDS_SemTake

    任务没有获取到信号量,共有2个任务被阻塞。

    MDS_SemGive

    一个任务获取到信号量,重新恢复到ready态,还有一个任务被阻塞。

    MDS_SemGive

    一个任务获取到信号量,重新恢复到ready态,没有任务被阻塞。

    MDS_SemGive

    没有任务获取信号量,信号量被置为满状态。

    MDS_SemGive

    没有任务获取信号量,信号量仍为满状态。

    表 9  信号量操作与二进制信号量空满状态的对应关系

    一个信号量可以阻塞多个任务,当信号量为空时,任何任务使用MDS_SemTake函数都可能被阻塞到该信号量上。当信号量上有被阻塞的任务时,如果一个任务使用MDS_SemGive函数释放了信号量,那么在这些被阻塞的任务中将会有一个任务被激活,从pend态恢复到ready态,重新参与任务调度。但具体是哪个任务先从pend状态恢复,我们可以采用两种调度方式,一种是先进先出(FIFO)方式,即最先被阻塞的任务最先被从pend状态恢复,另一种是优先级(PRIO)方式,即被阻塞的任务中优先级最高的任务最先被恢复。前面介绍的操作系统的任务调度方式就是优先级方式,因此在信号量里我们仍可以使用与任务调度相同的ready表结构来实现信号量的优先级调度,每个信号量里都有一个类似ready表的调度表,当任务被信号量阻塞时,任务被从ready表拆除,被挂接到信号量的调度表中,当信号量被释放时,激活信号量调度表中的最高优先级任务,将它从信号量表拆除,挂接到ready表中,对信号量调度表的拆除、添加过程与对任务调度表的拆除、添加过程是一样的。

    信号量结构如下所示:

    typedef struct m_sem

    {

        M_TASKSCHEDTAB strSemTab;       

        U32 uiCounter;                  

        U32 uiSemOpt;                   

    }M_SEM;

    其中M_TASKSCHEDTAB结构就是ready表的结构,可以将被阻塞的任务按照任务调度的方式挂接到strSemTab变量上。uiCounter变量用来表明信号量是空还是满。uiSemOpt变量用来表明信号量是采用FIFO还是PRIO调度方式。

    当信号量采用FIFO调度方式时,它只需要一个根节点就可以了,所有被阻塞的任务按照阻塞的顺序挂接到尾节点上,任务恢复时从头节点依次取出。在FIFO调度方式中,我们只使用strSemTab中优先级0的根节点作为FIFO方式的根节点就可以了。

     

    在获取信号量时,如果暂时获取不到信号量,那么有的情况可能需要任务一直处于pend状态,一直等待获取到信号量后才重新返回ready状态重新参与任务调度。而有的情况则可能会需要设定一个超时上限,如果任务在超时时间内获取到信号量,那么任务会返回ready状态重新参与调度,如果超时时间耗尽时还没有获取到信号量,那么任务也会重新转换为ready态重新参与任务调度。而有的情况则可能不需要做任何时间等待,任务获取不到信号量的话也需要继续运行。

     

    上面列出了二进制信号量所需要实现的所有功能,下面我们来看看代码的具体实现过程。

    在使用信号量前需要先定义一个M_SEM型的信号量变量,使用信号量初始化函数MDS_SemCreate对该变量进行初始化:

    00019  U32 MDS_SemCreate(M_SEM* pstrSem, U32 uiSemOpt, U32 uiInitVal)

    00020  {

    00021      

    00022      if(NULL == pstrSem)

    00023      {

    00024          return RTN_FAIL;

    00025      }

    00026  

    00027      

    00028      if((SEMFIFO != (SEMSCHEDOPTMASK & uiSemOpt))

    00029         && (SEMPRIO != (SEMSCHEDOPTMASK & uiSemOpt)))

    00030      {

    00031          return RTN_FAIL;

    00032      }

    00033  

    00034      

    00035      if((SEMEMPTY != uiInitVal) && (SEMFULL != uiInitVal))

    00036      {

    00037          return RTN_FAIL;

    00038      }

    00039  

    00040      

    00041      MDS_TaskSchedTabInit(&pstrSem->strSemTab);

    00042  

    00043      

    00044      pstrSem->uiCounter = uiInitVal;

    00045  

    00046      

    00047      pstrSem->uiSemOpt = uiSemOpt;

    00048  

    00049      return RTN_SUCD;

    00050  }

    00019行,函数返回值包括RTN_SUCD,表明创建信号量成功,RTN_FAIL表明创建信号量失败。入口参数pstrSem为需要初始化的信号量的指针,入口参数uiSemOpt为创建信号量所使用的选项,创建先进先出的信号量时使用SEMFIFO选项,创建优先级的信号量时使用SEMPRIO信选项。uiInitVal是信号量的初始值,SEMEMPTY表明创建的信号量的初始值为空,SEMFULL表明创建的信号量的初始值为满。

    00022~00025行,对入口参数pstrSem进行检查,若为空则返回失败。

    00028~00032行,对入口参数uiSemOpt进行检查,若既不是FIFO状态也不是PRIO状态则返回失败。

    00035~00038行,对入口参数uiInitVal进行检查,若信号量初始化值既不是空也不是满则返回失败。

    00041行,初始化信号量中的调度表,这个过程与任务中初始化ready表的过程是一致的。

    00044行,初始化信号量的初始值。

    00047行,初始化信号量的参数。

     

    获取信号量的函数MDS_SemTake的代码如下:

    00069  U32 MDS_SemTake(M_SEM* pstrSem, U32 uiDelayTick)

    00070  {

    00071      

    00072      if(NULL == pstrSem)

    00073      {

    00074          return RTN_FAIL;

    00075      }

    00076  

    00077      (void)MDS_IntLock();

    00078  

    00079      

    00080      gpstrCurTcb->pstrSem = pstrSem;

    00081  

    00082      

    00083      if(SEMNOWAIT == uiDelayTick)

    00084      {

    00085          

    00086          if(SEMFULL == pstrSem->uiCounter)

    00087          {

    00088              

    00089              pstrSem->uiCounter = SEMEMPTY;

    00090  

    00091              (void)MDS_IntUnlock();

    00092  

    00093              return RTN_SUCD;

    00094          }

    00095          else 

    00096          {

    00097              (void)MDS_IntUnlock();

    00098  

    00099              return RTN_SMTKRT;

    00100          }

    00101      }

    00102      else 

    00103      {

    00104          

    00105          if(SEMFULL == pstrSem->uiCounter)

    00106          {

    00107              

    00108              pstrSem->uiCounter = SEMEMPTY;

    00109  

    00110              (void)MDS_IntUnlock();

    00111  

    00112              return RTN_SUCD;

    00113          }

    00114          else 

    00115          {

    00116              

    00117              if(RTN_FAIL == MDS_TaskPend(pstrSem, uiDelayTick))

    00118              {

    00119                  (void)MDS_IntUnlock();

    00120  

    00121                  

    00122                  return RTN_FAIL;

    00123              }

    00124  

    00125              (void)MDS_IntUnlock();

    00126  

    00127              

    00128              MDS_TaskSwiSched();

    00129  

    00130              

    00131              return gpstrCurTcb->strTaskOpt.uiDelayTick;

    00132          }

    00133      }

    00134  }

    00069行,函数有5种返回值,RTN_SUCD:在延迟时间内获取到信号量。RTN_FAIL:获取信号量失败。RTN_SMTKTO:信号量时间耗尽,超时返回。RTN_SMTKRT:任务延迟状态被中断,没有获取到信号量。RTN_SMTKDL:信号量被删除。入口参数pstrSem为需要操作的信号量的指针。入口参数uiDelayTick为获取不到信号量时的超时时间,超时时间分为SEMNOWAITSEMWAITFEV和任意数值这3种类型,SEMNOWAIT是不等待,若获取不到信号量则立刻执行下条指令,SEMWAITFEV是永久等待,若获取不到信号量则永久处于pend状态,任意数值为pend的超时时间,单位为tick,若在此时间内获取到信号量,则任务重新返回ready态参与任务调度,若超时时间耗尽了还没有获取信号量,那么任务也重新返回ready态参与任务调度,这两种情况的返回值不同,用户可以根据返回值做相应的处理。

    00072~00075行,对入口参数pstrSem进行检查,若为空则返回失败。

    00077行,锁中断,防止多个任务同时操作信号量。

    00080行,将阻塞任务的信号量赋给TCB中相关的变量。本节在TCB中新增加了一个M_SEM*型的变量,

    typedef struct m_tcb

    {

        STACKREG strStackReg;           

        M_TCBQUE strTcbQue;             

        M_TCBQUE strDelayQue;           

        M_SEM* pstrSem;                 

        U8* pucTaskName;                

        U32 uiTaskFlag;                 

        U8 ucTaskPrio;                  

        M_TASKOPT strTaskOpt;           

        U32 uiStillTick;                

    }M_TCB;

    用它记录阻塞任务的信号量,这样,我们就可以通过这个变量从TCB中找到阻塞任务的信号量,进而找到信号量的调度表,然后就可以对阻塞这个任务的信号量的调度表进行操作了。

    00083行,pend时间为0走此分支。

    00086行,若信号量处于满状态走此分支。

    00089行,信号量为满状态,可获取到信号量,任务获取到信号量后,将信号量置为空状态。

    00091行,对信号量操作完毕,解锁中断。

    00093行,对信号量操作完毕,返回RTN_SUCD

    00095行,信号量为空走此分支。

    00097行,走此分支说明信号量为空无法获取,并且pend的时间为SEMNOWAIT,说明任务不需要进入pend状态,因此不对信号量做任何处理,直接解锁中断,准备返回。

    00099行,返回RTN_SMTKRT值,表明任务没有获取到信号量,直接返回。

    00102行,被pend的任务需要等待时间走此分支。

    00105行,若信号量处于满状态走此分支。

    00108行,信号量为满状态,可获取到信号量,任务获取到信号量后,将信号量置为空状态。

    00110行,对信号量操作完毕,解锁中断。

    00112行,对信号量操作完毕,返回RTN_SUCD

    00114行,信号量为空走此分支。

    00117行,走此分支说明信号量为空无法获取,需要等待一定时间以获取信号量,调用MDS_TaskPend函数操作各种调度表,将当前任务阻塞到pstrSem信号量上,阻塞时间为uiDelayTick

    00119行,阻塞任务操作发生错误,在函数返回前先解锁中断。

    00122行,对信号量操作失败,返回RTN_FAIL

    00125行,任务已经被阻塞,函数返回前先解锁中断。

    00128行,各种调度表在117MDS_TaskPend函数里已经更新完毕,此处需要调用软中断函数进行任务调度。

    00131行,走到此行,说明任务已经从running态切换为pend态,又从pend态切换回running态,该函数的返回值已经被存入到当前任务TCBstrTaskOpt.uiDelayTick变量中,返回函数的返回值。

     

    MDS_TaskPend函数与MDS_TaskDelay函数的处理过程非常相似,主要是将任务从ready表拆除,添加到delay表中,细节不再做介绍,请读者自行参考代码:

    00439  U32 MDS_TaskPend(M_SEM* pstrSem, U32 uiDelayTick)

    00440  {

    00441      M_CHAIN* pstrChain;

    00442      M_CHAIN* pstrNode;

    00443      M_CHAIN* pstrDelayNode;

    00444      M_TCBQUE* pstrTaskQue;

    00445      M_PRIOFLAG* pstrPrioFlag;

    00446      U8 ucTaskPrio;

    00447  

    00448      

    00449      if(gpstrCurTcb == gpstrIdleTaskTcb)

    00450      {

    00451          return RTN_FAIL;

    00452      }

    00453  

    00454      

    00455      ucTaskPrio = gpstrCurTcb->ucTaskPrio;

    00456      pstrChain = &gstrReadyTab.astrChain[ucTaskPrio];

    00457      pstrPrioFlag = &gstrReadyTab.strFlag;

    00458  

    00459      

    00460      pstrNode = MDS_TaskDelFromSchedTab(pstrChain, pstrPrioFlag, ucTaskPrio);

    00461  

    00462      

    00463      gpstrCurTcb->strTaskOpt.ucTaskSta &= ~((U8)TASKREADY);

    00464  

    00465      

    00466      gpstrCurTcb->strTaskOpt.uiDelayTick = uiDelayTick;

    00467  

    00468      

    00469      if(SEMWAITFEV != uiDelayTick)

    00470      {

    00471          gpstrCurTcb->uiStillTick = guiTick + uiDelayTick;

    00472  

    00473          

    00474          pstrTaskQue = (M_TCBQUE*)pstrNode;

    00475          pstrDelayNode = &pstrTaskQue->pstrTcb->strDelayQue.strQueHead;

    00476  

    00477          

    00478          MDS_TaskAddToDelayTab(pstrDelayNode);

    00479  

    00480          

    00481          gpstrCurTcb->uiTaskFlag |= DELAYQUEFLAG;

    00482      }

    00483  

    00484      

    00485      MDS_TaskAddToSemTab(gpstrCurTcb, pstrSem);

    00486  

    00487      

    00488      gpstrCurTcb->strTaskOpt.ucTaskSta |= TASKPEND;

    00489  

    00490      return RTN_SUCD;

    00491  }

     

    MDS_TaskPend函数中所使用的MDS_TaskAddToSemTab函数的功能是将任务添加到信号量调度表中,如果信号量采用优先级调度方式,则使用MDS_TaskAddToSchedTab函数添加,这个过程与将任务添加到ready表的过程是一样的。如果信号量采用先进先出调度方式,则将任务添加到链表的尾节点上。这个过程比较简单,不再详细介绍,代码如下:

    00353  void MDS_TaskAddToSemTab(M_TCB* pstrTcb, M_SEM* pstrSem)

    00354  {

    00355      M_CHAIN* pstrChain;

    00356      M_CHAIN* pstrNode;

    00357      M_PRIOFLAG* pstrPrioFlag;

    00358      U8 ucTaskPrio;

    00359  

    00360      

    00361      if(SEMPRIO == (SEMSCHEDOPTMASK & pstrSem->uiSemOpt))

    00362      {

    00363          

    00364          ucTaskPrio = pstrTcb->ucTaskPrio;

    00365          pstrChain = &pstrSem->strSemTab.astrChain[ucTaskPrio];

    00366          pstrNode = &pstrTcb->strTcbQue.strQueHead;

    00367          pstrPrioFlag = &pstrSem->strSemTab.strFlag;

    00368  

    00369          

    00370          MDS_TaskAddToSchedTab(pstrChain, pstrNode, pstrPrioFlag, ucTaskPrio);

    00371      }

    00372      else 

    00373      {

    00374          

    00375          pstrChain = &pstrSem->strSemTab.astrChain[LOWESTPRIO];

    00376          pstrNode = &pstrTcb->strTcbQue.strQueHead;

    00377  

    00378          

    00379          MDS_ChainNodeAdd(pstrChain, pstrNode);

    00380      }

    00381  }

    展开全文
  • linux二进制信号量的概念

    千次阅读 2016-12-28 13:09:41
    二进制信号量实现任务互斥: 打印机资源只有一个,abc三个任务共享,当a取得使用权后,为了防止其他任务错误地释放了信号量(),必须将打印机房的门关起来(进入临界段),用完后,释放信号量,再把门打开(出临界段),...
    互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。 二进制信号量,一个任务申请成功后,可以由另一个任务释放。二进制信号量实现任务互斥: 打印机资源只有一个,abc三个任务共享,当a取得使用权后,为了防止其他任务错误地释放了信号量(),必须将打印机房的门关起来(进入临界段),用完后,释放信号量,再把门打开(出临界段),其他任务再进去打印。(而互斥型信号量由于必须由取得信号量的那个任务释放,故不会出现其他任务错误地释放了信号量的情况出现,故不需要有临界段。互斥型信号量是二进制信号量的子集。)
     二进制信号量实现任务同步: a任务一直等待信号量,b任务定时释放信号量,完成同步功能
    互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。 二进制信号量,一个任务申请成功后,可以由另一个任务释放。二进制信号量实现任务互斥: 打印机资源只有一个,abc三个任务共享,当a取得使用权后,为了防止其他任务错误地释放了信号量(),必须将打印机房的门关起来(进入临界段),用完后,释放信号量,再把门打开(出临界段),其他任务再进去打印。(而互斥型信号量由于必须由取得信号量的那个任务释放,故不会出现其他任务错误地释放了信号量的情况出现,故不需要有临界段。互斥型信号量是二进制信号量的子集。) 二进制信号量实现任务同步: a任务一直等待信号量,b任务定时释放信号量,完成同步功能    
    展开全文
  •   二进制信号量:最快的最常用的信号量,可用于同步或互斥。 互斥信号量:为了解决内在的互斥问题如优先级继承、删除安全和递归等情况而最优化的特殊的二进制信号量。 计数信号量:类似于...
  • 二进制信号量和互斥量之间是否有任何区别,或者它们基本相同?
  • taskspawn创建的任务是进程,不是线程,在vxworks里Task等价于进程。 死循环方式是可以的,比如exctask等很多核心的task都是死循环 进程之间的内存空间是互相隔离的(虽然vxworks里也可以访问),进程内不同线程...
  • 二进制信号量

    2020-09-09 17:33:39
    二进制信号量的基本操作函数在线程与线程之间、中断与线程之间的操作过程如下图所示。 SylixOS 二进制信号量 1.creat 二进制信号量主要应用在以下场合: 有允许线程访问的一个资源,使用二进制信号量作为...
  • 关于二进制信号量

    千次阅读 2010-01-16 09:30:00
    今天刚看到二进制信号量的问题,有些疑问请教大家:1,用共享资源时需要用二进制信号量互斥,我的问题是,程序不是顺序执行的嘛,那在一个时间段只有一个程序可以访问共享资源,这个程序完了 ,下个程序才能访问。...
  • 互斥和二进制信号量的使用

    千次阅读 2012-10-31 18:33:49
    1. 二进制信号量 semBCreate (SEM_Q_FIFO/SEM_Q_PRIORITY , SEM_EMPTY/SEM_FULL) 有两个作用: (1)任务间的互斥 -- 同一个任务获取和释放信号量,防止两个任务同时存取一个资源 (2)任务间的同步 -- 一...
  • 互斥型信号量必须是同一个... 二进制信号量,一个任务申请成功后,可以由另一个任务释放。 互斥型信号量是二进制信号量的子集。 二进制信号量实现任务同步: a任务一直等待信号量,b任务定时释放信号量,完成同步功能
  • virtual void functionName() = 0;这样形式的语句说明这个函数为纯虚函数,没有具体的函数体,只有一个函数名,函数体留给派生类来实现。如果一个类里面至少含有一个纯虚函数,那么,不可以用这个类定义对象。...
  • URL: http://blog.csdn.net/hxg130435477/article/details/5998006
  • 03_FreeRTOS 使用二进制信号量 本文介绍: 二进制信号量的使用方法 简介信号量 Binary Semaphore之所以称为Binary信号量,是因为它存在(‘1’)或不存在(‘0’)。其中没有第三个条件。因此,任务要么具有信号...
  • vxworks系统学习----二进制信号量

    千次阅读 2016-07-29 14:52:33
    在wind内核中存在二进制信号量、互斥信号量及计 数信号量。互斥:当共享地址空间进行简单的数据交换时,为避免竞争需要对内存进行互锁,即多个任务访问共享内存时,体现出来的排它性。使用二进制信号量就可以很方便...
  • 互斥信号量和二进制信号量的区别(转)2009年12月03日 星期四 09:50 A.M.互斥信号量和二进制信号量的区别 互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。 二进制...
  • VxWorks的信号量机制分析   VxWorks信号量是提供任务间通信、同步和互斥的最优...互斥信号量:为了解决内在的互斥问题如优先级继承、删除安全和递归等情况而最优化的特殊的二进制信号量。 计数信号量:类似于二进制
  • URL: http://bbs.ednchina.com/BLOG_ARTICLE_145889.HTM
  • 二进制信号量 互斥信号量 计数器信号量 SylixOS 信号量 多个线程在读写某个共享数据(全局变量等)时必须通过某种方法实现共享数据的互斥访问或者同步访问(例如线程 B 等待线程 A 的结果以继续运行)。其中,...
  • Linux线程同步()之使用信号量

    千次阅读 2017-10-12 11:42:04
    信号量的概念1、信号量是一个特殊类型的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作,即使在一个多线程...2、最简单的信号量是二进制信号量,它只有0和1两种取值。还有更通用的信号量——计数信号量,
  • VxWorks信号量小结

    千次阅读 2018-04-04 17:22:44
    信号量是VxWorks中任务间通信的一种基本手段。VxWorks提供了三种信号量:二进制信号量、互斥信号量和计数信号量。...二进制信号量 创建二进制信号量的函数:SEM_ID semBCreate( int options , SEM_B_STATE i...
  • vxworks信号量分析

    2014-04-26 10:31:33
    VxWorks的信号量机制分析   VxWorks信号量是提供任务间通信、同步和互斥的最优选择,提供任务间最...互斥信号量:为了解决内在的互斥问题如优先级继承、删除安全和递归等情况而最优化的特殊的二进制信号量
  • UCOSIII信号量

    2017-12-27 13:16:51
    二进制信号量 计数型信号量 二进制信号量 二进制信号量只能取0和1两个值计数型信号量 计数型信号量的范围由OS_SEM_CTR决定。OS_SEM_CTR可以为8位,16位和32位API函数//创建 void OSSemCreate (OS_SEM *p_sem, ...
  • STM32CubeMX FreeRTOS信号量实验

    千次阅读 2018-02-06 11:57:46
    系统:linux mint xfce 64bit 软件: 1、SW4STM32 ...本次实验信号量的作用是中断与任务间同步,使用STM32CubeMX配置的freertos其API进行了封装,是CMSIS格式的API,关于信号量的操作函数如

空空如也

1 2 3 4 5 ... 20
收藏数 71,712
精华内容 28,684
关键字:

二进制信号量