精华内容
下载资源
问答
  • 互斥信号量为0表示
    千次阅读
    2021-05-08 16:40:32

    临界资源必须是互斥的访问,即一段时间内只能让一个进程访问,对应临界区。
    p(s)—申请资源—wait(s)
    v(s)—释放资源—signal(s)

    为使多个进程能互斥的访问某临界资源,只需为该资源设置一互斥信号量mutex,并设初始值为1,然后将各进程访问该资源的临界区置于wait(mutex)和signal(mutex)操作之间即可。

    互斥信号量值的描述:
    初值为1,取值范围为(-1,0,1)。
    当 mutex=1 时,表示两个进程皆未进入需要互斥的临界区;
    当 mutex=0 时,表示有一个进程进入临界区运行,另外一个必须等待,改入阻塞队列;
    当 mutex=-1时,表示有一个进程正在临界区运行,另一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒。

    更多相关内容
  • 信号量和互斥信号量

    2022-03-28 17:47:59
    在可剥夺内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被成武优先级反转,为了解决优先级反转这个问题,UCOSIII引入互斥信号量。 信号量 优先级反转 ...

    信号量用来控制任务存取共享资源,实现任务间的同步以及任务和ISR(中断)间同步。

    在可剥夺内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被成武优先级反转,为了解决优先级反转这个问题,UCOSIII引入互斥信号量

    信号量

    信号量分为两种:二进制信号量计数型信号量,二进制信号量只能取0和1两个值,计数型信号量不止可以取2个值,在共享资源中只有任何可以使用信号量,中断服务程序则不能使用。

    1.创建信号量

    void   OSSemCreate(OS_SEM     *p_sem,      //指向信号量控制块,定义一个全局信号量,并将信号量的 
                                               //指针传递给函数
                       CPU_CHAR   *p_name,     //信号量的名字
                       OS_SEM_CTR  cnt,        //信号量的初始值,如果为1,代表信号量为二进制信号 
                                               //量,大于1的话就代表此信号量为计数型信号量
                       OS_ERR      *p_err)     //返回错误码

    2.请求信号量

    OS_SEM_CTR   OSSemPend(OS_SEM     *p_sem, 
                           OS_TICK    timeout,
                           OS_OPT     opt,
                           CPU_TS     *p_ts,
                           OS_ERR     *p_err) 

    p_sem:指向一个信号量的指针

    timeout:指定等待信号量的超时时间,如果在指定时间内没有等到信号量则任务恢复执行。如果指定时间为0的话,任务就会一直等待下去,知道等到信号量。

    opt:用于设置是否使用阻塞模式,有两个选项:OS_OPT_PEND_BLOCKING指定信号量无效时,任务挂起以等待信号量;OS_OPT_PEND_NON_BLOCKING信号量无效时,任务直接返回。

    p_ts:指向一个时间戳,用来记录接收到信号量的时刻,如果给这个参数赋值NULL,则说明用户没有要求时间戳。

    p_err:保存调用本函数后返回的错误码。

    3.发送信号量(释放信号量)

    OS_SEM_CTR   OSSemPost(OS_SEM     *p_sem, //指向一个信号量的指针
                           OS_OPT     opt,    //用来选择信号量发送的方式
                           OS_ERR     *p_err) //用来保存调用此函数后返回的错误码

    opt:用来选择信号量发送的方式。

    OS_OPT_POST_1                     仅向等待该信号量的优先级最高的任务发送信号量。

    OS_OPT_POST_ALL                 向等待该信号量的所有任务发送信号量。

    OS_OPT_POST_NO_SCHED   该选项禁止在本函数内执行任务调度操作,即使该函数使得更高优先级的任务结束挂起进入就绪状态,也不会执行任务调度,而是会在其他后续函数中完成任务调度。

    优先级反转

     

    互斥信号量

    为了避免优先级反转,UCOSIII支持一种特殊的二进制信号量:互斥信号量。

    1.  任务H与任务M处于挂起状态,等待某一事件的发生,任务L正在运行中。
    2. 某一时刻任务L想要访问共享资源,在此之前它必须先获得对应资源的互斥型信号。
    3. 任务L获得互斥型信号量并开始使用该共享资源。
    4. 由于任务H优先级高,它等待的事件发生后便剥夺了任务L的CPU使用权。
    5. 任务H开始运行。
    6. 任务H运行过程中也要使用任务L在使用的资源,考虑到任务L正在占用着资源,UCOSIII会将任务L的优先级升至通任务H一样,使得任务L能继续执行而不被其他中等优先级的任务打断。
    7. 任务L以任务H的优先级继续运行,注意此时任务H并没有运行,因为任务H在等待任务L释放掉互斥信号量。
    8. 任务L完成所有的任务,并释放掉互斥型信号量,UCOSIII会自动将任务L的优先级恢复到提升之前的值,然后UCOSIII会将互斥型信号量给正在等待着的任务H。
    9. 任务H获得互斥信号量开始执行。
    10. 任务H不再需要访问共享资源,于是释放掉互斥型信号量。
    11. 由于没有更高优先级的任务需要执行,所以任务H继续执行。
    12. 任务H完成所有工作,并等待某一事件发生,此时UCOSIII开始运行在任务H或者任务L运行过程中已经就绪的任务M。
    13. 任务M继续执行。

    注意!只有任务才能使用互斥信号量(中断服务程序则不可以)

    创建互斥型信号量

    void   OSMutexCreate(OS_MUTEX   *p_mutex,    //指向互斥型信号量控制块
                         CPU_CHAR   *p_name,     //互斥信号量的名字
                         OS_ERR     *p_err       //调用此函数后返回的错误码
                        )

    p_mutex:指向互斥型信号量控制块。互斥型信号量必须有用户程序进行实际分配,可以使用如下所示代码。OS_MUTEX    MyMutex;

    p_name:互斥信号量的名字

    p_err:调用此函数后返回的错误码

    请求互斥型信号量

    void   OSMutexPend(OS_MUTEX   *p_mutex,
                       OS_TICK     timeout,
                       OS_OPT      opt,                    
                       CPU_TS      *p_ts,
                       OS_ERR      *p_err)

    p_mutex:指向互斥信号量

    timeout:指定等待互斥信号量的超时时间(时钟节拍数),如果在指定的时间内互斥信号量没有释放,则允许任务恢复允许。该值设置为0的话,表示任务将会一直等待下去,直到信号量被释放掉。

    opt:用于选择是否使用阻塞模式。

         OS_OPT_PEND_BLOCKING指定互斥信号量被占用时,任务挂起等待该互斥信号量。

         OS_OPT_PEND_NON_BLOCKING指定当互斥信号量被占用时,直接返回任务。

    p_ts:指向一个事件戳,记录发送、终止或删除互斥信号量的时刻。

    p_err:用于保存调用此函数后返回的错误码。

    发送互斥信号量

    void   OSMutexPost(OS_MUTEX   *p_mutex,
                       OS_OPT      opt,
                       OS_ERR     *p_err)

    p_mutex:指向互斥信号量

    opt:用来指定是否进行任务调度操作

            OS_OPT_POST_NONE   不指定特定的选项

            OS_OPT_POST_NO_SCHED   禁止在本函数内执行任务调度操作

    p_err:用来保存调用此函数返回的错误码

    任务内嵌信号量

    在UCOSIII中每个任务都有自己的内嵌的信号量。

     

    等待任务信号量

    OS_SEM_CTR   OSTaskSemPend(OS_TICK   timeout,
                               OS_OPT    opt,
                               CPU_TS    *p_ts,
                               OS_ERR    *p_err)

    timeout:如果在指定的节拍数内没有收到信号量任务就会因为等待超时而恢复运行,如果timeout为0的话任务就会一直等待,直到收到信号量。

    opt:用于选择是否使用阻塞模式。OS_OPT_PEND_BLOCKING指定互斥信号量被占用时,任务挂起等待该互斥信号量;OS_OPT_PEND_NON_BLOCKING指定当互斥信号量被占用时,直接返回任务。

    p_ts:指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。

    p_err:调用此函数后返回的错误码

    发布任务信号量

    OS_SEM_CTR   OSTaskSemPost(OS_TCB   *p_tcb,
                               OS_OPT    opt,
                               OS_ERR   *p_err)

    p_tcb:指向要用信号通知的任务的TCB,当设置为NULL的时候可以向自己发送信号量。

    opt:用来指定是否进行任务调度操作     OS_OPT_POST_NONE不指定特定的选项;OS_OPT_POST_NO_SCHED禁止在本函数内执行任务调度操作。

    p_err:调用此按时后返回的错误码。

    展开全文
  • 同步信号量和互斥信号量

    千次阅读 2022-05-16 10:26:11
    互斥信号量的初值一般设1 2、用途 (1)同步信号量的用途:防止被抢占 初始空 低优先级的任务持有信号量,高优先级的任务需要这个信号量,只有当低优先级的任务give(释放)信号量,高优先级的任务才能take...

    1、初值
    同步信号量的初值一般设为0;
    互斥信号量的初值一般设为1

    2、用途
    (1)同步信号量的用途:防止被抢占 初始为空
    低优先级的任务持有信号量,高优先级的任务需要这个信号量,只有当低优先级的任务give(释放)信号量,高优先级的任务才能take(获取)信号量。通过这种机制低优先级的任务就可以防止被高优先级的任务抢占。give和take是分别在两个任务里做的。

    (2)互斥信号量的用途:对临界区上锁 初始为满
    当一个任务想对临界区访问时,为了防止别的任务也对该临界区操作,它需要对该临界区上锁,即take(获取)一个互斥的信号量,以保证独享。当该任务take(获取)一个互斥的信号量以后,它仍然能被高优先级的任务抢占,但高优先级的用户仍然无法访问它已经上锁的临界区。而解锁也是由上锁的任务来做的。take和give是在一个任务里完成的。

    3、值的含义
    (1)同步信号量,值为资源可以使用的个数,信号量小于0,则线程进行等待,信号量大于0,表示可用资源个数。初始值0.
    (2)互斥信号量只有两个值0或1,0表示资源正在被占用,线程等待。1表示,资源没有被使用,线程可以进入。初始值为1

    展开全文
  • 一、互斥信号量简介 ...互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先

    一、互斥信号量简介

    互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的"优先级翻转"的影响降到最低。优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:
    1、互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
    2、中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

    二、互斥信号量相关API

    FreeRTOS提供了两个互斥信号量创建函数,如下表所示:

    函数描述
    xSemaphoreCreateMutex()使用动态方法创建互斥信号量
    xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量

    1、函数xSemaphoreCreateMutex()
    此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数xQueueCreateMutex(),此函数原型如下:

    SemaphoreHandle_t xSemaphoreCreateMutex(void)
    
    参数描述
    返回值NULL:互斥信号量创建失败。其他值:创建成功的互斥信号量的句柄。

    2、函数xSemaphoreCreateMutexStatic()
    此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的,RAM需要由用户来分配,此函数是个宏,具体创建过程是通过函数xQueueCreateMutexStatic()来完成的,函数原型如下:

    SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t*pxMutexBuffer)
    
    参数描述
    pxMutexBuffer此参数指向一个StaticSemaphore_t类型的变量,用来保存信号量结构体。
    返回值NULL:互斥信号量创建失败。其他值:创建成功的互斥信号量的句柄。

    三、互斥信号量相关函数详细分析

    1、互斥信号量创建过程分析
    动态创建互斥信号量函数xSemaphoreCreateMutex(),此函数是个宏,定义如下:

    #define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)
    

    可以看出,真正干事的是函数xQueueCreateMutex(),此函数在文件queue.c中有如下定义,

    QueueHandle_t xQueueCreateMutex( 
    const uint8_t ucQueueType )
    {
    Queue_t *pxNewQueue;
    const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
    
    	pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );(1)
    	prvInitialiseMutex( pxNewQueue );(2)
    
    	return pxNewQueue;
    }
    
    • (1)、调用函数xQueueGenericCreate()创建一个队列,队列长度为1,队列项长度为0,队列类型为参数ucQueueType。由于本函数是创建互斥信号量的,所以参数ucQueueType为queueQUEUE_TYPE_MUTEX。
    • (2)、调用函数prvInitialiseMutex()初始化互斥信号量。

    其中prvInitialiseMutex()代码如下:

    static void prvInitialiseMutex( Queue_t *pxNewQueue )
    {
    	if( pxNewQueue != NULL )
    	{
    /*虽然创建队列的时候会初始化队列结构体的成员变量,但是现在是创建互斥信号量,有些成员需要重新赋值,特别是用于优先级继承的*/
    		pxNewQueue->pxMutexHolder = NULL;(1)
    		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;(2)
    
    		//如果是递归互斥信号量
    		pxNewQueue->u.uxRecursiveCallCount = 0;(3)
    
    		traceCREATE_MUTEX( pxNewQueue );
    
    		//释放互斥信号量
    		( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
    	}
    	else
    	{
    		traceCREATE_MUTEX_FAILED();
    	}
    }
    

    (1)和(2)、这里大家可能会疑惑,队列结构体Queue_t中没有pxMutexHolder和uxQueueType这两个成员变量?这两个东西是哪里来的妖孽?这两个其实是宏,专门为互斥信号量准备的,在文件queue.c中有如下定义:

    #define pxMutexHolder pcTail
    #define uxQueueType pcHead
    #define queueQUEUE_IS_MUTEX NULL
    

    当Queue_t用于表示队列的时候pcHead和pcTail指向队列的存储区域,当Queue_t用于表示互斥信号量的时候就不需要pcHead和pcTail了。当用于互斥信号量的时候将pcHead指向NULL来表示pcTail保存着互斥队列的所有者,pxMutexHolder指向拥有互斥信号量的那个任务的任务控制块。重命名pcTail和pcHead就是为了增强代码的可读性。
    (3)、如果创建的互斥信号量是互斥信号量的话,还需要初始化队列结构体中的成员变量u.uxRecursiveCallCount。
    互斥信号量创建成功以后会调用函xQueueGenericSend()释放一次信号量,说明互斥信号量默认就是有效的!互斥信号量初始化完成如下图所示:
    在这里插入图片描述
    2、释放互斥信号量过程分析
    释放互斥信号量的时候和二值信号量、计数型信号量一样,都是用的函数xSemaphoreGive()(实际上完成信号量释放的是函数xQueueGenericSend()。)不过由于互斥信号量涉及到优先级继承的问题,所以具体处理过程会有点区别。使用函数xSemaphoreGive()释放信号量最重要的一步就是将uxMessagesWaiting加一,而这一步就是通过函数
    prvCopyDataToQueue()来完成的,释放信号量的函数xQueueGenericSend()会调用prvCopyDataToQueue()。互斥信号量的优先级继承也是在函数prvCopyDataToQueue()中完成的,此函数中有如下一段代码:

    static BaseType_t prvCopyDataToQueue( 
    Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )
    {
    BaseType_t xReturn = pdFALSE;
    UBaseType_t uxMessagesWaiting;
    	uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
    	{
    	//互斥信号量
    		#if ( configUSE_MUTEXES == 1 )
    		{
    			if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )(1)
    			{
    				xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );(2)
    				pxQueue->pxMutexHolder = NULL;(3)
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		#endif /* configUSE_MUTEXES */
    	}
    	/**********省略其他代码************/
    	
    
    • (1)、当前操作的是互斥信号量。
    • (2)、调用函数xTaskPriorityDisinherit()处理互斥信号量的优先级继承问题。
    • (3)、互斥信号量释放以后,互斥信号量就不属于任何任务了,所以pxMutexHolder要指向NULL。

    现在来看一下函数xTaskPriorityDisinherit()是怎么具体的处理优先级继承的,函数xTaskPriorityDisinherit0代码如下:

    BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
    {
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
    BaseType_t xReturn = pdFALSE;
    
    	if( pxMutexHolder != NULL )(1)
    	{
    		/* 当一个任务获取到互斥信号量就会涉及优先级继承的问题,正在释放互斥信号量的任务肯定是现在正在运行的任务pxCurrentTCB */
    		configASSERT( pxTCB == pxCurrentTCB );
    
    		configASSERT( pxTCB->uxMutexesHeld );
    		( pxTCB->uxMutexesHeld )--;(2)
    
    		/* 是否存在优先级继承?如果存在的话任务当前优先级肯定和任务基优先级不同*/
    		if( pxTCB->uxPriority != pxTCB->uxBasePriority )(3)
    		{
    			/* 当前任务只获取到一个互斥信号量 */
    			if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )(4)
    			{
    				if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )(5)
    				{
    					taskRESET_READY_PRIORITY( pxTCB->uxPriority );(6)
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 使用新的优先级将任务重新添加到就绪列表中 */
    				traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
    				pxTCB->uxPriority = pxTCB->uxBasePriority;(7)
    				listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );(8) 
    				prvAddTaskToReadyList( pxTCB );(9)
    				xReturn = pdTRUE;(10)
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	else
    	{
    		mtCOVERAGE_TEST_MARKER();
    	}
    
    	return xReturn;
    }
    
    • (1)、函数的参数pxMutexHolder表示拥有此互斥信号量任务控制块,所以要先判断此互斥信号量是否已经被其他任务获取。
    • (2)、有的任务可能会获取多个互斥信号量,所以就需要标记任务当前获取到的互斥信号量个数,任务控制块结构体的成员变量uxMutexesHeld用来保存当前任务获取到的互斥信号量个数。任务每释放一次互斥信号量,变量uxMutexesHeld肯定就要减一。
    • (3)、判断是否存在优先级继承,如果存在的话任务的当前优先级肯定不等于任务的基优先级。
    • (4)、判断当前释放的是不是任务所获取到的最后一个互斥信号量,因为如果任务还获取了其他互斥信号量的话就不能处理优先级继承。优先级继承的处理必须是在释放最后一个互斥信号量的时候。
    • (5)、优先级继承的处理说白了就是将任务的当前优先级降低到任务的基优先级,所以要把当前任务先从任务就绪表中移除。当任务优先级恢复为原来的优先级以后再重新加入到就绪表中。
    • (6)、如果任务继承来的这个优先级对应的就绪表中没有其他任务的话就将取消这个优先级的就绪态。
    • (7)、重新设置任务的优先级为任务的基优先级uxBasePriority。
    • (8)、复位任务的事件列表项。
    • (9)、将优先级恢复后的任务重新添加到任务就绪表中。
    • (10)、返回pdTRUE,表示需要进行任务调度。

    3、获取互斥信号量过程分析
    获取互斥信号量的函数同获取二值信号量和计数型信号量的函数相同,都是xSemaphoreTake()(实际执行信号量获取的函数是xQueueGenericReceive()),获取互斥信号量的过程也需要处理优先级继承的问题,函数xQueueGenericReceive()在文件queue.c中有定义,在缩减后的函数代码如下:

    BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, 
    void * const pvBuffer, TickType_t xTicksToWait, 
    const BaseType_t xJustPeeking )
    {
    BaseType_t xEntryTimeSet = pdFALSE;
    TimeOut_t xTimeOut;
    int8_t *pcOriginalReadPosition;
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;
    	for( ;; )
    	{
    		taskENTER_CRITICAL();
    		{
    			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    		//判断队列是否有消息
    			if( uxMessagesWaiting > ( UBaseType_t ) 0 )(1)
    			{
    				pcOriginalReadPosition = pxQueue->u.pcReadFrom;
    
    				prvCopyDataFromQueue( pxQueue, pvBuffer );(2)
    
    				if( xJustPeeking == pdFALSE )(3)
    				{
    					traceQUEUE_RECEIVE( pxQueue );
    
    					//移除消息
    					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;(4)
    
    					#if ( configUSE_MUTEXES == 1 )(5)
    					{
    						if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
    						{
    							pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); (6)
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					#endif /* configUSE_MUTEXES */
    /*查看是否有任务因为入队而阻塞,如果有的话就需要解除阻塞态。*/
    					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )(7)
    					{
    						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
    						{
    /*如果解除任务阻塞的任务优先级比当前任务优先级高的话就需要进行一次任务切换*/
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				else(8)
    				{
    					traceQUEUE_PEEK( pxQueue );
    //读取队列中的消息以后需要删除消息
    					pxQueue->u.pcReadFrom = pcOriginalReadPosition;
    //如果有任务因为出队而阻塞的话就解除任务的阻塞态
    					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )(9)
    					{
    						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
    						{
    //如果解除阻塞的任务优先级比当前任务优先级高的话就需要进行一次任务切换
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    
    				taskEXIT_CRITICAL();
    				return pdPASS;
    			}
    			else //队列为空 (10)
    			{
    				if( xTicksToWait == ( TickType_t ) 0 )
    				{
    	/*队列为空,如果阻塞时间为0的话就直接返回errQUEUE_EMPTY*/
    					taskEXIT_CRITICAL();
    					traceQUEUE_RECEIVE_FAILED( pxQueue );
    					return errQUEUE_EMPTY;
    				}
    				else if( xEntryTimeSet == pdFALSE )
    				{
    	//队列为空且设置了阻塞时间,需要初始化时间状态结构体。
    					vTaskSetTimeOutState( &xTimeOut );
    					xEntryTimeSet = pdTRUE;
    				}
    				else
    				{
    					/* Entry time was already set. */
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    		}
    		taskEXIT_CRITICAL();
    		vTaskSuspendAll();
    		prvLockQueue( pxQueue );
    
    //更新时间状态结构体,并且检查超时是否发送。
    		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )(11)
    		{
    			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )(12)
    			{
    				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
    
    				#if ( configUSE_MUTEXES == 1 )
    				{
    					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )(13)
    					{
    						taskENTER_CRITICAL();
    						{
    							vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );(14)
    						}
    						taskEXIT_CRITICAL();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				#endif
    
    				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );(15)
    				prvUnlockQueue( pxQueue );
    				if( xTaskResumeAll() == pdFALSE )
    				{
    					portYIELD_WITHIN_API();
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    			else//再试一次
    			{
    				prvUnlockQueue( pxQueue );
    				( void ) xTaskResumeAll();
    			}
    		}
    		else
    		{
    			prvUnlockQueue( pxQueue );
    			( void ) xTaskResumeAll();
    
    			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
    			{
    				traceQUEUE_RECEIVE_FAILED( pxQueue );
    				return errQUEUE_EMPTY;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    	}
    }
    
    • (1)、队列不为空,可以从队列中提取数据。
    • (2)、调用函数prvCopyDataFromQueue()使用数据拷贝的方式从队列中提取数据。
    • (3)、数据读取以后需要将数据删除掉。
    • (4)、队列的消息数量计数器uxMessagesWaiting减一,通过这一步就将数据删除掉了。
    • (5)、表示此函数是用于获取互斥信号量的。
    • (6)、获取互斥信号量成功,需要标记互斥信号量的所有者,也就是给pxMutexHolder赋值,pxMutexHolder应该是当前任务的任务控制块。但是这里是通过函数pvTaskIncrementMutexHeldCount()来赋值的,此函数很简单,只是将任务控制块中的成员变量uxMutexesHeld加一,表示任务获取到了一个互斥信号量,最后此函数返回当前任务的任务控制块。
    • (7)、出队成功以后判断是否有任务因为入队而阻塞的,如果有的话就需要解除任务的阻塞态,如果解除阻塞的任务优先级比当前任务的优先级高还需要进行一次任务切换。
    • (8)、出队的时候不需要删除消息。
    • (9)、如果出队的时候不需要删除消息的话那就相当于刚刚出队的那条消息接着有效!既然还有有效的消息存在队列中,那么就判断是否有任务因为出队而阻塞,如果有的话就解除任务的阻塞态。同样的,如果解除阻塞的任务优先级比当前任务的优先级高的话还需要进行一次任务切换。
    • (10)、上面分析的都是队列不为空的时候,那当队列为空的时候该如何处理呢?处理过程和队列的任务级通用入队函数xQueueGenericSend()类似。如果阻塞时间为0的话就就直接返回errQUEUE_EMPTY,表示队列空,如果设置了阻塞时间的话就进行相关的处理。
    • (11)、检查超时是否发生,如果没有的话就需要将任务添加到队列的xTasksWaitingToReceive列表中。
    • (12)、检查队列是否继续为空?如果不为空的话就会在重试一次出队。
    • (13)、表示此函数是用于获取互斥信号量的。
    • (14)、调用函数vTaskPriorityInherit()处理互斥信号量中的优先级继承问题,如果函数xQueueGenericReceive()用于获取互斥信号量的话,此函数执行到这里说明互斥信号量正在被其他的任务占用。此函数和函数xTaskPriorityDisinherit()过程相反。此函数会判断当前任务的任务优先级是否比正在拥有互斥信号量的那个任务的任务优先级高,如果是的话就会把拥有互斥信号量的那个低优先级任务的优先级调整为与当前任务相同的优先级
    • (15)、经过(12)步判断,队列依旧为空,那么就将任务添加到列表xTasksWaitingToReceive中。

    互斥信号量的讲解就到这里啦!!!谢谢大家收看!!!

    展开全文
  • OSMutexCreate()创建互斥信号量2.OSMutexPend()请求互斥信号量3.OSMutexPost()函数释放互斥信号量总结 一、信号量简介 与FreeRTOS相同信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获
  • 本文介绍了递归互斥信号量的概念和API函数源码分析、以及递归互斥信号量的使用实例
  • UCOSIII——信号量和互斥信号量详解

    千次阅读 2020-08-07 16:15:41
    为了解决这个问题,引出了互斥信号量的概念。 1:信号量   信号量是一种上锁机制,该任务必须获得相应的钥匙才能进入,直至代码执行结束,释放钥匙,程序才退出。信号量分两种,一种是二进制信号量,一种是计数型
  • 操作系统~同步信号量和互斥信号量二者的区别

    千次阅读 多人点赞 2020-04-11 23:18:31
    互斥信号量的初值一般设1 2、用途 (1)同步信号量的用途:防止被抢占 初始空 低优先级的任务持有信号量,高优先级的任务需要这个信号量,只有当低优先级的任务give(释放)信号量,高优先级的任务才能take...
  • 进程同步 由于进程调度的存在,进程具有异步性的特征,即并发执行的进程以各自独立、不可预知的速度向前推进,进程之间没有明确的先后执行顺序。 而像管道通信这种进程...互斥共享是一个时间段只能有一个进程访问该资
  • FreeRTOS系列|互斥信号量

    千次阅读 2021-04-14 21:37:54
    本文介绍了优先级翻转问题、互斥信号量的概念和API函数源码分析、以及如何使用互斥信号量来解决优先级翻转的实例
  • } 操作系统正是利用信号量的状态来对进程和资源进行管理的   1.3 利用信号量实现进程之间的互斥   设置一个互斥信号量mutex,初值1,表示该临界资源空闲。   调用P(mutex)申请临界资源——mutex变为0。  ...
  • 一、信号量简介 相比于自旋锁,信号量可以使线程进入休眠状态,比如A与B、C合租了一套房子,这个房子只有一个厕所,一次只能一个人使用。某一天早上A去上厕所了,过了一会B也想用厕所,因为A在厕所里面,所以B只能...
  • 互斥锁和信号量

    千次阅读 2021-08-14 18:17:26
    POSIX标准中进程和线程同步和互斥的方法,主要有信号量互斥锁两种方式。 同步:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。 同步就是在互斥的基础上有顺序 二、互斥锁 2.1 互斥锁...
  • 1、互斥锁(mutex) 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。注意:同一时刻,只能有一个线程持有该锁。 当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C...
  • C++ 多线程中互斥信号量的使用

    千次阅读 2017-07-09 23:10:08
    互斥信号量mutex 互斥量使用示例互斥信号量mutex互斥信号量是一种内核对象,它用来保证一个线程独占一个资源的访问。在使用互斥信号量的时候,有四个主要的函数: (1)CreateMutex,创建互斥信号量。函数原型如下...
  • (2)只有获得互斥信号量的任务才能释放互斥信号量,所以中断上下文中不能释放互斥信号量。 (3)支持嵌套请求,即获得互斥信号量的任务可再次请求该互斥信号量。若嵌套请求信号量,则每请求一次将消耗一个信号量。...
  • 互斥量和信号量的理解

    千次阅读 2018-07-29 15:59:56
    互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。   Mutex本质上说...
  • 互斥量与信号量互斥与同步)

    千次阅读 2017-12-20 19:10:09
    互斥量(Mutex) 互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步...值为0, 表示锁定状态,
  • 信号量实现同步互斥经典案例

    千次阅读 2021-01-11 19:48:06
    这一篇在上篇的基础上得以延伸,介绍一下相对复杂的进程互斥、同步的案例。 问题描述 系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并...
  • 二进制信号量互斥量之间的区别

    千次阅读 2019-12-20 09:55:08
    二进制信号量互斥量之间是否有任何区别,或者它们基本相同?
  • linux的互斥量和信号量

    千次阅读 2019-05-21 21:49:49
    linux的信号量:一般是指一个信号灯 比如我有一个车库那么 我进来一量车 那么就一个信号灯进行加一 如果走出去那么就进行减一 当加一的时候 其他的车无法进来 这里我设的是一个信号灯 当我通过sem_init函数进行设置...
  • 互斥锁Mutex和信号量

    千次阅读 2018-06-11 14:27:57
    1、Mutex 互斥量/互斥锁Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex的主要作用是用于互斥的访问共享资源。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0表示锁定...
  • 【UCOSIII】UCOSIII的互斥信号量

    万次阅读 2018-07-02 21:18:54
    信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用(不太清楚的可以参考链接:【UCOSIII】UCOSIII的信号量)。   优先级反转 优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种...
  • 信号量&同步与互斥

    2018-04-08 11:28:19
    在介绍信号量&同步与互斥之前,我们先来了解一下生产者与消费者模型。 生产者将数据放入缓冲区,消费者将数据从缓冲区取走消费。 生产者消费者的模型提出了三种关系,两种角色,一个场所 三种关系: ...
  • 二值信号量和互斥信号量的区别

    千次阅读 2014-06-30 16:00:23
    互斥信号量和二进制信号量的区别   互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。    二进制信号量,一个任务申请成功后,可以由另一个任务释放。  ...
  • 他出名是因为图论中著名的“最短路径”算法,因为早期关于结构化编程的论战“Goto语句是有害的”,还因为他引入了名为信号量的同步原语,正是这里我们要学习的。事实上,Dijkstra及其同事发明了信号量,作为与同步...
  • 互斥量和信号量的区别

    万次阅读 多人点赞 2018-04-25 23:36:11
    互斥量和信号量的区别1. 互斥量用于线程的互斥信号量用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但...
  • 信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个...
  • 信号量互斥量解析

    千次阅读 2014-10-31 10:09:09
    首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:  P(S):①将信号量S的值减1,即S=S-1;  ②如果S³0,则该进程继续执行;否则该进程...
  • 互斥信号量 如果有三个任务ABC,优先级的顺序也是ABC,A和C共用一个信号。如果在C占用信号的时候,A处于等待状态,当B处于就绪状态的时候就会打断C任务的执行,从而影响A任务得到信号量。这样B任务就会出现优先级的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 50,686
精华内容 20,274
热门标签
关键字:

互斥信号量为0表示

友情链接: lrecog-master.zip