补充 8.8 设备驱动中的阻塞与非阻塞 IO 等.

Signed-off-by: chen.yang <chen.yang@yuzhen-iot.com>
This commit is contained in:
chen.yang 2022-04-14 20:12:20 +08:00
parent 0489e739bd
commit d85646aede
2 changed files with 103 additions and 0 deletions

View File

@ -293,8 +293,46 @@ void complete_all(struct completion *c);
### 读写信号量
读写信号量与信号量的关系与读写自旋锁和自旋锁的关系类似,读写信号量可能引起进程阻塞,但它可允许 N 个读执行单元同时访问共享资源,而最多只能有一个写执行单元。
```cpp
// 定义读写信号量
struct rw_semaphore my_rws;
// 初始化读写信号量
void init_rwsem(struct rw_semaphore *sem);
// 获取读信号量
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
// 释放读信号量
void up_read(struct rw_semaphore *sem);
// 获取写信号量
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
// 释放写信号量
void up_write(struct rw_semaphore *sem);
```
## 互斥体
尽管信号量已经可以实现互斥的功能,而且包含 DECLARE_MUTEX()、init_MUTEX ()等定义信号量的宏或函数,从名字上看就体现出了互斥体的概念,但是 mutex 在 Linux 内核中还是真实地存在的。
```cpp
// 码定义名为互斥体
struct mutex my_mutex;
// 初始化互斥体
mutex_init(&my_mutex);
// 获取互斥体,不能被信号打断
void fastcall mutex_lock(struct mutex *lock);
// 获取互斥体,可以被信号打断
int fastcall mutex_lock_interruptible(struct mutex *lock);
// 尝试获取互斥体,获取失败立即返回
int fastcall mutex_trylock(struct mutex *lock);
// 释放互斥体
void fastcall mutex_unlock(struct mutex *lock);
```
## 并发控制示例
```cpp

View File

@ -0,0 +1,65 @@
# 8.8 设备驱动中的阻塞与非阻塞 IO
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
阻塞从字面上听起来似乎意味着低效率,实则不然,如果设备驱动不阻塞,则用户想获取设备资源只能不停地查询,这反而会无谓地耗费 CPU 资源。而阻塞访问时,不能获取资源的进程将进入休眠,它将 CPU 资源让给其他进程。
因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。
## 等待队列
可以使用等待队列wait queue来实现阻塞进程的唤醒它以队列为基础数据结构与进程调度机制紧密结合能够用于实现内核中的异步事件通知机制。
### 等待队列头
```cpp
// 定义等待队列头
wait_queue_head_t my_queue;
// 初始化等待队列头
init_waitqueue_head(&my_queue);
// 初始化并声明等待队列头
DECLARE_WAIT_QUEUE_HEAD(name)
```
### 定义和添加/移除等待队列
```cpp
// 定义等待队列
DECLARE_WAITQUEUE(name, tsk)
// 添加/移除等待队列
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
```
add_wait_queue() 用于将等待队列 wait 添加到等待队列头 q 指向的等待队列链表中,而 remove_wait_queue() 用于将等待队列 wait 从附属的等待队列头 q 指向的等待队列链表中移除。
### 等待事件
```cpp
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
```
等待第一个参数 queue 作为等待队列头的等待队列被唤醒,而且第二个参数 condition 必须满足否则阻塞。wait_event() 和 wait_event_interruptible() 的区别在于后者可以被信号打断而前者不能。加上_timeout 后的宏意味着阻塞等待的超时时间,以 jiffy 为单位,在第三个参数的 timeout 到达时,不论 condition 是否满足,均返回。
### 唤醒队列
```cpp
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
```
上述操作会唤醒以 queue 作为等待队列头的所有等待队列中所有属于该等待队列头的等待队列对应的进程。
wake_up() 应 与 wait_event() 或 wait_event_timeout() 成对使用,而 wake_up_interruptible() 则应与 wait_event_interruptible() 或 wait_event_interruptible_timeout() 成对使用。wake_up() 可唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的进程,而 wake_up_interruptible() 只能唤醒处于 TASK_INTERRUPTIBLE 的进程。
### 在等待队列上睡眠
```cpp
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
```
sleep_on() 函数的作用就是将目前进程的状态置成 TASK_UNINTERRUPTIBLE并定义一个等待队列之后把它附属到等待队列头 q直到资源可获得q 引导的等待队列被唤醒。interruptible_sleep_on() 与 sleep_on() 函数类似,其作用是将目前进程的状态置成 TASK_INTERRUPTIBLE并定义一个等待队列之后把它附属到等待队列头 q直到资源可获得q 引导的等待队列被唤醒或者进程收到信号。sleep_on() 函数应该与 wake_up() 成对使用interruptible_sleep_on() 应该与 wake_up_interruptible()成对使用。