嵌入式学习笔记 - freeRTOS为什么中断中不能使用互斥量
freeRTOS为什么中断中不能使用互斥量,这是因为互斥量主要体现的就是优先级继承,而优先级继承对于中断而言并无意义,要理解这个问题先要从互斥量的机制说起:
一 互斥量优先级继承机制
如上图,任务L,M,H优先级依次升高,任务L与任务H共用一个信号量资源,如果某一时刻任务L拥有了这个共享资源,任务H要处于阻塞状态等待任务L,假设此时不需要共享资源的任务M时间到了,则会打断任务L,那么任务L的释放信号量的时间将会更长,那么高优先级的任务H就很长时间得不到执行,这显然不符合操作系统的优先级机制。
互斥量机制就解决这个问题,假如任务H获取共享资源时,任务L正在使用,那么此时任务H会将任务L的优先级提升到任务H一样的值,那么任务L会一直运行不会被中间级别优先级的任务打断,这就大大缩短了任务L释放共享资源的时间,如下图
二 那么这跟中断有什么关系?
关键就在发起人,就是提升优先级的那个时刻是谁要求的,因为互斥量使用中,提升优先级的要求的是发起人啊,就是想要获取共享资源因其他任务正在使用而获取不到的那个发起人,先暂且不说是任务或者中断,统一叫发起人,下面讲这个发起人为什么不能是中断,
先讲中断作为持有者的情况,再讲作为发起人的情况
1)如果中断作为持有者
这种情况更容易理解,如果作为持有者,中断的优先级已经很高,甚至高于任何任务,会尽快释放,不存在优先级反转问题。但是最关键的还是程序机制,中断在获取到互斥量时,无法将本身的优先级赋值给互斥量,因为它没有任务属性,没有优先级,
下面是获取互斥量的程序执行,也在消息队列通用接收函数xQueueGenericReceive()里面,最开始一段。
if( uxMessagesWaiting > ( UBaseType_t ) 0 ) //互斥量没有被取走,可以取
{
pcOriginalReadPosition = pxQueue->u.pcReadFrom;
prvCopyDataFromQueue( pxQueue, pvBuffer );
if( xJustPeeking == pdFALSE )
{
traceQUEUE_RECEIVE( pxQueue );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); //这就是关键的互斥量优先级赋值语句,这个语句的作用就是将当前任务持有的互斥量的数量加一(这个不是这里要讲的),然后将获取到互斥量的当前任务的控制块指针赋值给互斥量的pxMutexHolder成员(这个是这里要将的),
如下图,那么因为中断没有任务属性,更没有没有任务控制块,无法将当前获取到互斥量的中断的任务控制块块赋值给互斥量(因为中断没有任务块啊),这个任务控制块后面会用来提升持有互斥量的任务的优先级(被高优先级任务获取互斥量时)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
2)如果中断作为获取方(也就是发起人)
当别的任务持有互斥量时,中断想要获取互斥量不能获取时,尽管此时道理上可以提升持有者任务的优先级,但是提升任务持有者的优先级要把“当前任务”本身优先级赋值给它,中断不具有任务属性没有任务控制块更没有优先级可言,无法给持有互斥量的任务优先级赋值新值,所以不适用。
下面是优先级继承的程序执行,在消息队列通用接收函数xQueueGenericReceive()里面后面一段获取不到信号量如何处理的部分,互斥量的获取最终也是通过这个函数执行:
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )//如果获取不到互斥量进入此处,也就是第一次进入xQueueGenericReceive()函数,检测到互斥量不在(被别的任务取走),进入此处
{
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER_CRITICAL();
{
vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );//这就是关键的优先级继承语句,下图是内部执行,具体过程就是将“当前任务”的优先级赋值给互斥量持有者的任务,而当前任务不是某个任务,是中断,中断是没有任务属性的,没有任务控制块,也就没有任务优先级可言,所以无法赋值。
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
所以由此看来互斥量是专门为任务设置的机制。
三 那么信号量为什么有在中断使用的情况?
因为信号量获取或释放后任务状态可能会发生变化,处于阻塞态的一些任务可能会因为得到信号量而变为就绪态,就需要任务切换,这里处于阻塞态的想要切换的任务是被动的(不是当前的)任务,不是当前的执行者(当前的执行者是指任务或者中断,),那么中断中就可以设置一个任务切换要求的标志位,当前的执行者是为了别的任务而开设的任务切换标志位。中断使用可以使用信号量释放,而且可以设置一个任务切换要求标志。
也就说信号量的获得者可以是中断,但是互斥量的获得者不能是中断。
通过搜索百度AI也证实了这一结果:
四 deepseek 关于为什么中断不能接收互斥量的信号的解答:
优先级继承机制无意义
本质原因:中断的优先级与任务调度无关,而互斥量的核心机制(优先级继承)仅适用于任务间资源竞争的场景。
详细解释
-
互斥量的核心机制
- 互斥量通过优先级继承解决优先级反转问题:当高优先级任务因低优先级任务持有互斥量而阻塞时,低优先级任务的优先级会被临时提升至高优先级任务的水平,确保其快速释放资源。
- 关键限制:优先级继承的触发依赖于“发起请求的任务”,而中断并非任务,无法参与优先级调度。
-
中断的特殊性
- 中断的优先级由硬件决定,始终高于任何任务,无需通过继承机制提升优先级。
- 中断不允许阻塞,若等待互斥量会导致系统响应延迟或死锁。
-
信号量与互斥量的区别
- 二值/计数信号量:仅同步状态,无优先级继承逻辑,因此可在中断中释放(如通知任务处理事件)。
- 互斥量:必须由任务持有,中断无法成为“持有者”或“请求者”。
延伸场景
若中断需访问共享资源,可通过以下替代方案:
- 在中断中释放二值信号量,由高优先级任务实际处理资源。
- 使用关中断/开中断保护临界区(需谨慎避免影响实时性)。
总结:互斥量的设计初衷是任务间资源管理,中断因其非任务特性和硬件优先级机制,与互斥量的功能逻辑不兼容