From 0489e739bd80a6a99229ff4cfcf0bef38bcb9c5d Mon Sep 17 00:00:00 2001 From: "chen.yang" Date: Thu, 14 Apr 2022 09:07:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E9=87=8F=20=E9=83=A8=E5=88=86.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen.yang --- .../8.7_设备驱动中的并发控制.md | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Chapter8_SOC_与_Linux/8.7_设备驱动中的并发控制.md b/Chapter8_SOC_与_Linux/8.7_设备驱动中的并发控制.md index 5be7628..e7742e7 100644 --- a/Chapter8_SOC_与_Linux/8.7_设备驱动中的并发控制.md +++ b/Chapter8_SOC_与_Linux/8.7_设备驱动中的并发控制.md @@ -262,7 +262,34 @@ void up(struct semaphore * sem); 如果信号量被初始化为 0,则它可以用于同步,同步意味着一个执行单元的继续执行需等待另一执行单元完成某事,保证执行的先后顺序。 -### 完成量 +### 完成量(completion) + +Linux 系统提供了一种比信号量更好的同步机制,即完成量,它用于一个执行单元等待另一个执行单元执行完某事。 + +```cpp +// 定义完成量。 +struct completion my_completion; +// 初始化完成量。 +init_completion(&my_completion); +// 定义并初始化完成量。 +DECLARE_COMPLETION(my_completion); +// 等待一个 completion 被唤醒。 +void wait_for_completion(struct completion *c); +// 唤醒一个等待的执行单元。 +void complete(struct completion *c); +// 唤醒所有等待的执行单元。 +void complete_all(struct completion *c); +``` + +### 自旋锁与信号量的对比 + +信号量是进程级的,用于多个进程之间对资源的互斥,虽然也是在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的。如果竞争失败,会发生进程上下文切换,当前进程进入睡眠状态,CPU 将运行其他进程。鉴于进程上下文切换的开销也很大,因此,只有当进程占用资源时间较长时,用信号量才是较好的选择。 + +当所要保护的临界区访问时间比较短时,用自旋锁是非常方便的,因为它节省上下文切换的时间。但是 CPU 得不到自旋锁会在那里空转直到其他执行单元解锁为止,所以要求锁不能在临界区里长时间停留,否则会降低系统的效率。 + +信号量所保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区。因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一个进程企图获取本自旋锁,死锁就会发生。 + +信号量存在于进程上下文,因此,如果被保护的共享资源需要在中断或软中断情况下使用,则在信号量和自旋锁之间只能选择自旋锁。当然,如果一定要使用信号量,则只能通过 down_trylock()方式进行,不能获取就立即返回以避免阻塞。 ### 读写信号量