当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]互斥量和信号量长得像,用法像,连API都像——但它们的设计意图完全相反。互斥量管的是"谁能进这扇门",信号量管的是"还剩几张票"。用错了,轻则优先级反转死锁,重则整个系统卡死。90%的开发者把二进制信号量当互斥量用,这是FreeRTOS里最贵的一个错误。

互斥量和信号量长得像,用法像,连API都像——但它们的设计意图完全相反。互斥量管的是"谁能进这扇门",信号量管的是"还剩几张票"。用错了,轻则优先级反转死锁,重则整个系统卡死。90%的开发者把二进制信号量当互斥量用,这是FreeRTOS里最贵的一个错误。

原理:一字之差,天壤之别

互斥量的核心是"所有权"。 谁拿走了互斥量,谁就必须还。任务A拿走互斥量进入临界区,任务B来抢,阻塞。任务A还回互斥量,任务B才能拿。这个"拿了就得还"的约束,让互斥量天生具备两个信号量没有的能力:优先级继承。

优先级继承是这样工作的:低优先级任务L持有互斥量,高优先级任务H来抢,L被阻塞。此时中等优先级任务M开始运行,把L挤下去了——H永远等不到互斥量,这就是优先级反转。互斥量的解决方案是:当H抢互斥量时,L临时提升到H的优先级,把M压下去,L迅速执行完临界区还回互斥量,H立刻拿到,L再降回原优先级。整个过程自动完成,开发者不用写一行代码。

信号量的核心是"计数"。 它不管谁拿的,只管还剩多少。二进制信号量(计数为1)看起来和互斥量一样——拿了再还,但它没有所有权概念。任务A拿了二进制信号量,任务B也能拿(如果计数允许),还的时候谁还都行。没有所有权,就没有优先级继承。所以用二进制信号量保护临界资源,一旦发生优先级反转,系统只能硬扛。

计数信号量更不一样。 它管理的是资源池。比如串口只有一个,计数信号量初始值设为1,谁拿到谁用,用完还回,下一个接着拿。它不保护临界区,它管理并发访问的许可数量。

一句话总结:互斥量保护资源,信号量同步事件。互斥量有主人,信号量没主人。有主人的才能继承优先级。

程序说明:三种用法,三种API

用法一:互斥量保护共享变量——必须用互斥量

SemaphoreHandle_t xMutex;

int32_t shared_counter = 0;

void vCounterTask(void *pvParameters) {

for (;;) {

xSemaphoreTake(xMutex, portMAX_DELAY); // 拿锁,有优先级继承

shared_counter++; // 临界区

xSemaphoreGive(xMutex); // 还锁,必须是同一个任务还

vTaskDelay(10);

}

}

关键约束:Give和Take必须成对出现在同一个任务里。 互斥量记录了持有者是谁,Task A拿的必须Task A还。如果Task A拿了,Task B还了——FreeRTOS直接触发断言,因为所有权被破坏了。

用法二:二进制信号量同步任务——不能用互斥量

SemaphoreHandle_t xBinarySem;

void vISR_Handler(void) {

BaseType_t xHigherPriorityTaskWoken = pdFALSE;

xSemaphoreGiveFromISR(xBinarySem, &xHigherPriorityTaskWoken);

portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

}

void vHandlerTask(void *pvParameters) {

for (;;) {

xSemaphoreTake(xBinarySem, portMAX_DELAY); // 等中断通知

// 处理事件

}

}

这里必须用二进制信号量,不能用互斥量。原因是ISR里只能调用FromISR版本的API,而互斥量没有FromISR的Give——中断不能还互斥量,因为中断不是任务,没有"所有权"的概念。用信号量,ISR给,任务取,天经地义。

用法三:计数信号量管理资源池——互斥量干不了这活

SemaphoreHandle_t xPoolSem;

#define MAX_CONNECTIONS 3

void vInit(void) {

xPoolSem = xSemaphoreCreateCounting(MAX_CONNECTIONS, MAX_CONNECTIONS);

}

void vClientTask(void *pvParameters) {

if (xSemaphoreTake(xPoolSem, 100) == pdTRUE) {

// 占用一个连接

UseConnection();

xSemaphoreGive(xPoolSem); // 释放,任意任务都能还

}

}

三个客户端同时抢,计数从3减到0,第四个阻塞。任意一个用完释放,计数加一,下一个醒来。这里用互斥量完全行不通——互斥量只有"有/没有"两种状态,管不了"还剩几个"。

正确使用说明:三条铁律

铁律一:保护临界资源,只用互斥量。 共享变量、硬件寄存器、文件句柄——凡是"同一时间只能一个人碰"的东西,必须用互斥量。用二进制信号量替代,等于主动放弃优先级继承,优先级反转时只能靠运气。

铁律二:ISR同步,只用二进制信号量。 中断通知任务"事件来了",这是信号量的本职工作。互斥量没有FromISR的Give接口,强行用会编译报错。

铁律三:资源池管理,只用计数信号量。 连接池、缓冲区池、事件队列容量——凡是"最多N个并发"的场景,计数信号量是唯一正确选择。互斥量没有计数能力,强行用只能管住第一个,管不住第二个。

最致命的错误是反过来:用互斥量做事件同步。任务A等事件,用互斥量——任务B给事件,还互斥量。看起来能跑,但如果任务B优先级比A低,A永远等不到,因为互斥量的优先级继承只在"抢锁"时触发,"给锁"时不触发。正确做法是用二进制信号量:A等信号量,B给信号量,没有所有权,谁给都行。

记住:互斥量问的是"这是谁的",信号量问的是"还剩多少"。问题不同,答案就不同。用错了,不是报错,是死锁。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

μC/OS-II是一个完整的,可移植、可固化、可裁减的占先式实时多任务内核,它功能强大,支持56个用户任务,支持信号量、邮箱、消息队列等多种常用的进程间通信机制。

关键字: 信号量 邮箱

摘要:随着我国智能电网的快速发展,传统采集设备已无法完全满足当前需求。配电台区是营销与生产的交汇点,传统模式下,营销与生产双方需要在台区侧分别安装集中器和配变终端(TTU)两种设备,但仍存在诸多问题。在此背景下,融合了I...

关键字: 能源控制器 信号量 死锁

衰落效应是影响无线通信质量的主要因素之一。其中的快衰落深度可达30~40dB,如果想利用加大发射功率、增加天线尺寸和高度等方法来克服这种深衰落是不现实的,而且会造成对其它电台的干扰。而采用分集方法即在若干个支路上接收相互...

关键字: 分集技术 信号量 衰减

摘要:阐述了一种基于GPRS和嵌入式Linux的远程图像监控系统设计和实现方法。该系统主要由嵌入式视频采集终端 和监控中心服务器组成。其中,嵌入式视频采集终端主要由摄像头视频采集模块、ARM模块、SIM900模块组成,监...

关键字: 通用分组无线业务 实时图像采集 多线程 信号量

FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量)。

关键字: FreeRTOS 信号量分析 信号量

1.互斥量用于线程的互斥,信号量用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对

关键字: 多线程 互斥量

 1 BOOL ReleaseSemaphore( 2  HANDLE hSemaphore, // 信号量句柄 3  LONG lReleaseCount, // 计数递增数量 4  LPLONG 

关键字: 信号量

 1 BOOL ReleaseSemaphore( 2  HANDLE hSemaphore, // 信号量句柄 3  LONG lReleaseCount, // 计数递增数量 4  LPLONG 

关键字: c++ mfc 信号量 semaphore csemaphore类

信号量的自带帮助文件是这样的:信号量用于限制可在同一个共享(受保护)资源上同时执行的任务数量。受保护的资源或关键代码段可能涉及全局变量的写入或与外部仪器通信。使用信号量可将两个或多个独立的并行任务同步

关键字: LabVIEW 信号量 同步

关于信号量的使用,程序自带帮助文件的说明是这样的:信号量(互斥量)是用来保护对共享资源进行访问的一个对象。被访问的共享资源的代码叫作关键临界段。在同一时间,只有一定数量的任务可以访问信号量。信号量以此

关键字: LabVIEW 信号量
关闭