各类 RTOS 都会涉及这些概念:任务通知(task notification)、队列(queue)、事件组
(event group)、信号量(semaphoe)、互斥量(mutex)等。
1.同步与互斥概念引入
举个栗子:例子1:在团队活动里,同事 A 先写完报表,经理 B 才能拿去向领导汇报。经理 B 必须等同事 A 完成报表, AB 之间有依赖, B 必须放慢脚步,被称为同步。例子2:在团队活动中,同事 A 已经使用会议室了,经理 B 也想使用,即使经理 B 是领导,他也得等着,这就叫互斥。经理 B 跟同事 A 说:你用完会议室就提醒我。这就是使用"同步"来实现"互斥"。
从本例子中可见,同步与互斥两者的联系。例子2中的会议室就好像临界的资源(同一时间只能有一个人使用的资源),同事A与经理B都可以获得,都是现在这种情况是互斥的,经理B只能等A用完才能用会议室,后面同事A提醒经理B,这里又用到了同步,同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息("发送信息"方法很多有:1.任务通知(task notification)、2.队列(queue)、3.事件组(event group)、4.信号量(semaphoe)、5.互斥量(mutex)等)。
2.实现同步、互斥的方法对比
能实现同步、互斥的内核方法有:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)。
其实它们都有类似的操作方法:获取/释放、阻塞/唤醒、超时。
举个例子:
- A 获取资源,用完后 A 释放资源
- A 获取不到资源则阻塞, B 释放资源并把 A 唤醒
- A 获取不到资源则阻塞,并定个闹钟;闹钟时间到, A 要么超时返回,或者要么在这段时间内因为 B 释放资源而被唤醒。
以下对这些概念(内核对象)进行形象的解读:
内核对象 | 生产者 | 消费者 | 数据/状态 | 说明 |
队列 | ALL | ALL | 数据:若干个数据 谁都可以往队列里扔数据, 谁都可以从队列里读数据 | 用来传递数据, 发送者、接收者无限制, 一个数据只能唤醒一个接 收者 |
事件组 | ALL | ALL | 多个位:或、与 谁都可以设置(生产)多个位, 谁都可以等待某个位、若干个 位 | 用来传递事件, 可以是 N 个事件, 发送者、接受者无限制, 可以唤醒多个接收者:像 广播 |
信号量 | ALL | ALL | 数量: 0~n 谁都可以增加一个数量, 谁都可消耗一个数量 | 用来维持资源的个数, 生产者、消费者无限制, 1 个资源只能唤醒 1 个接 收者 |
任务通知 | ALL | 只有我 | 数据、状态都可以传输, 使用任务通知时, 必须指定接受者 | N 对 1 的关系: 发送者无限制, 接收者只能是这个任务 |
互斥量 | 只能 A 开锁 | A 上锁 | 位: 0、 1 我上锁: 1 变为 0, 只能由我开锁: 0 变为 1 | 就像一个空厕所, 谁使用谁上锁, 也只能由他开锁 |
先看概念再使用图形对比:
O 队列:
里面可以放任意数据,可以放多个数据
任务、 ISR 都可以放入数据;任务、 ISR 都可以从中读出数据
O 事件组:
一个事件用一个bit 表示, 1 表示事件发生了, 0 表示事件没发生。
可以用来表示事件、事件的组合发生了,不能传递数据。
有广播效果:事件或事件的组合发生了,等待它的多个任务都会被唤醒。
O 信号量:
核心是"计数值"
任务、 ISR 释放信号量时让计数值加 1
任务、 ISR 获得信号量时,让计数值减 1
O 任务通知:
核心是任务的 TCB 里的数值
会被覆盖
发通知给谁?必须指定接收任务
只能由接收任务本身获取该通知
O 互斥量:
数值只有 0 或 1
谁获得互斥量,就必须由谁释放同一个互斥量,相当于一个任务有一个互斥量