4 同步互斥与通信
4.1 基本概念
一句话理解同步与互斥:我等你用完 厕所,我再用 厕所。
什么叫同步?就是:我正在用厕所,你等会。
什么叫互斥?就是:我正在用厕所,你不能进来。
同步与互斥经常放在一起讲,是因为它们之的关系很大, "互斥"操作可以使用"同步"来
实现。我"等"你用完厕所,我再用厕所。这不就是用"同步"来实现"互斥"吗?
同一时间只能有一个人使用的资源,被称为临界资源 。比如任务A、 B都要使用串口来
打印,串口就是临界资源。
4.2 实现互斥的问题
在裸机程序里,可以通过一个全局变量或静态变量实现互斥,比如要互斥使用LCD:
c
void LCD_Print(char *str)
{
static int available = 1;
if(available)
{
available = 0;
//使用LCD的代码
available = 1
}
}
但是在RTOS里,有概率在执行available = 0;之前就被切换出去(即在判断或赋值的时候被打断 ),导致被多个任务同时调用。
为了避免这种问题,可以通过disable_irq()来关闭中断以实现互斥:
c
void LCD_Print(char *str)
{
static int available = 1;
disable_irq();
if(available)
{
available = 0;
enable_irq();
//使用LCD的代码
available = 1
}
enable_irq();
}
要记得把中断再次打开。
在使用全局变量实现同步互斥的时候,又会因为编译器的特性导致一些问题,这就麻烦了。RTOS提供了实现同步互斥的方法,下面来介绍。
4.3 FreeRTOS实现同步互斥的方法
能实现同步、互斥的内核方法有:任务通知(task notification)、队列(queue)、事件组
(event group)、信号量(semaphoe)、互斥量(mutex)。
内核对象 | 生产者 | 消费者 | 数据/状态 | 说明 |
---|---|---|---|---|
队列 | ALL | ALL | 若干个数据,谁都可以读写队列 | 一个数据只能唤醒一个接收者 |
事件组 | ALL | ALL | 若干个位运算符 | 用来传递时间,可以唤醒多个接收者 |
信号量 | ALL | ALL | 数量 | 用来维持资源的个数,一个资源只能唤醒一个接收者 |
任务通知 | ALL | 自己 | 可以传输数据和状态,必须指定接收者 | N对1的关系,发送者无限制,接收者只能是被指定的那个 |
互斥量 | 只能A开锁 | A上锁 | bool值 | 谁上锁谁解锁 |
队列:
里面可以放任意数据,可以放多个数据
任务、ISR都可以放入数据;任务、ISR都可以从中读出数据
事件组:
一个事件用一 bit 表示, 1 表示事件发生了, 0 表示事件没发生
可以用来表示事件、事件的组合发生了,不能传递数据
有广播效果 :事件或事件的组合发生了,等待它的多个任务都会被唤醒
信号量:
核心是"计数值"
任务、ISR释放信号量时让计数值加1
任务、ISR获得信号量时,让计数值减1
任务通知:
核心是任务的TCB里的数值
会被覆盖
发通知给谁?必须指定接收任务
只能由接收任务本身获取该通知
互斥量:
数值只有0或1
谁获得互斥量,就必须由谁释放同一个互斥量