Skip to content

Latest commit

 

History

History
88 lines (73 loc) · 4.09 KB

1_并发与竞态——信号量与互斥体.md

File metadata and controls

88 lines (73 loc) · 4.09 KB
<style> h1 { text-align: center; border-left: 5px solid #e86422; border-right: 5px solid #e86422; } h2 { border-left: 5px solid #ff7f00; padding-left: 10px; } h3 { border-left: 5px solid #e86422; padding-left: 8px; } h4 { border-left: 3px solid #f0a000; padding-left: 5px; } </style>

Release log:

2021-04-27 二: 完成初版

原文地址

并发与竞态——信号量与互斥体

说明

前一段时间学习了《LINUX 设备驱动程序》中的“并发与竞态”章节,没过多久在阅读代码时,看到了 spin_lock_bh 这个函数,然后一脸瞢逼,这个函数后缀有啥意义来着?所以决定对这一章节做一个简单整理,一是为了加深印象,二是为了后续的快速回顾以及查找

资源共享的规则

  1. 只要可能,就应该避免资源的共享。如果我们将资源放在多个线程都会找到的地方,则必须有足够的理由
  2. 在单个执行线程之外共享硬件或软件资源的任何时候,因为另外一个线程可能产生对该资源的不一致观察,因此必须显式地管理对该资源的访问
  3. 当内核代码创建了一个可能和其他内核部分共享的对象时,该对象必须在还有其他组件引用自己时保持存在(并正确工作)

信号量和互斥体

在计算机科学中,信号量是一个众所周知的概念。一个信号量本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为 P 和 V。希望进入临界区的进程将在相关信号量上调用 P;如果信号量的值大于零,则该值减小一,而进程可以继续。相反,如果信号量的值为零(或更小),进程必须等待直到其他人释放该信号量。对信号量的解锁通常调用 V 完成;该函数增加信号量的值,并在必要时唤醒等待的进程

当信号量用于互斥时(即避免多个进程同时在一个临界区中运行),信号量的值应初始化为 1

Linux 内核中信号量的使用

在 Linux 内核中,要使用信号量需要包含头文件 <linux/semaphore.h>,相关的类型为 struct semaphore

信号量的声明以及初始化函数

DECLARE_MUTEX(name);
void sema_init(struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);

Linux 中,P 函数被称为 down,下面是他的几个版本:

void down(struct semaphore *sem); // 非可中断操作,用于建立不可杀进程(ps 输出中的 "D state")
int down_interruptible(struct semaphore *sem); // 可中断,被中断时返回非零值,调用者未拥有该信号量
int down_killable(struct semaphore *sem); // 可以被 fatal signal 打断
int down_trylock(struct semaphore *sem); // 不会休眠,如果信号量在调用时不可获取,立即返回非零值
int down_timeout(struct semaphore *sem, long timeout); // timeout 是以 jiffies 计数

Linux 中,等价与 V 的函数是 up

void up(struct semaphore *sem);

读取者/写入者信号量

读取者/写入者信号量被称为 rwsem,使用时需要包含 <linux/rwsem.h>,相关的数据类型为 struct rw_semaphore

rwsem 的初始化函数如下

void init_rwsem(struct rw_semaphore *sem);

rwsem 用于只读访问的操作如下

void down_read(struct rw_semaphore *sem); // 可能将进程至于不可中断的休眠中
int down_read_trylock(struct rw_semaphore *sem); // 成功返回 1,否则返回 0
void up_read(struct rw_semaphore *sem);

rwsem 用于写入者的操作如下

void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem); // 将写入者降级为读取者

写入者具有更高优先级,如果大量写入者竞争一个信号量,这种实现会导致读取者“饿死”