四、信号量管理
FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量)。我们可以把互斥量和递归互斥量看成特殊的信号量。互斥量和信号量在用法上不同:
(1)信号量用于同步,任务间或者任务和中断间同步;互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁。
(2)信号量用于同步时,一般是一个任务(或中断)给出信号,另一个任务获取信号;互斥量必须在同一个任务中获取信号、同一个任务给出信号。
(3)互斥量具有优先级继承,信号量没有。
(4)互斥量不能用在中断服务程序中,信号量可以。
(5)创建互斥量和创建信号量的API函数不同,但是共用获取和给出信号API函数;
可以将二进制信号量看作只有一个项目(item)的队列,因此这个队列只能为空或满(因此称为二进制)。任务和中断使用信号量无需关注谁控制---只需要知道二值信号量是空还是满。利用这个机制可以在任务和中断之间同步。
互斥资源的使用方式:
当一个任务在使用某个资源的过程中,即还没有完全结束对资源的访问时,便被切出运行态,使得资源处于非一致,不完整的状态。如果这个时候有另一个任务或者中断来访问这个资源,则会导致数据损坏或是其它相似的错误。
例子:访问外设考虑如下情形,有两个任务都试图往一个 LCD 中写数据:
任务 A 运行,并往 LCD 写字符串”Hello world”。
任务 A 被任务 B 抢占,但此时字符串才输出到”Hello w”。
任务 B 往 LCD 写”Abort, Retry, Fail?”,然后进入阻塞态。
任务 A 从被抢占处继续执行,完成剩余的字符输出——“orld”。 现在 LCD 显示的是被破坏了的字符串”HellowAbort, Retry, Fail?orld”。
1、基本临界区是指宏 taskENTER_CRITICAL()与 taskEXIT_CRITICAL()之间的代码区间。临界区的工作仅仅是简单地把中断全部关掉,抢占式上下文切换只可能在某个中断中完成,所以调用 taskENTER_CRITICAL()的任务可以在中断关闭的时段一直保持运行态,直到退出临界区。
坏处:临界区之间的代码必须简短,像上面例子这种中断输出是不行的。长时间没有任务切换影响系统的实时性。
2、挂起调度器。
通过调用 vTaskSuspendAll()来挂起调度器。挂起调度器可以停止上下文切换而不用关中断。如果某个中断在调度器挂起过程中要求进行上下文切换,则这个请求也会被挂起,直到调度器被唤醒后才会得到执行。xTaskResumeAll()解除调度器的挂起状态。
3、互斥量
使用xSemaphoreCreateMutex()API函数创建互斥量,xSemaphoreTake(xMutex, portMAX_DELAY )拿走信号量,xSemaphoreGive( xMutex )归还信号量。
使用互斥量需要避免进入死锁:
例子:任务A与任务B都需要获得互斥量X与互斥量Y以完成各自的工作:
1. 任务A执行,并成功获得了互斥量 X。
2. 任务 A 被任务 B 抢占。
3. 任务 B 成功获得了互斥量 Y,之后又试图获取互斥量 X——但互斥量 X 已 经被任务 A 持有,所以对任务 B 无效。任务 B 选择进入阻塞态以等待互斥量 X 被释放。
4. 任务A得以继续执行。其试图获取互斥量Y——但互斥量 Y 已经被任务B持有而对任务A无效。任务A也选择进入阻塞态以等待互斥量 Y 被释放。
这种情形的最终结局是,任务A在等待一个被任务B持有的互斥量,而任务B也在等待一个被任务A持有的互斥量。死锁于是发生,因为两个任务都不可能再执行下去了。
|