阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不
停地查询,直至可以进行操作为止。
一、linux设备驱动中的阻塞之等待队列
在 Linux 驱动程序中,可以使用等待队列( Wait Queue )来实现阻塞进程的唤醒。
在本例中使用如下:
当一个读驱动进程访问驱动buff为空时阻塞并等待写进程的唤醒。
当进程写驱动buff为满阻塞并等待读进程(读进程会取走一部分驱动buff空间)唤醒。
wait_queue_head_t my_queue;/*定义等待队列头部*/
init_waitqueue_head(&my_queue);/*初始化等待队列头部*/
DECLARE_WAIT_QUEUE_HEAD (name);/*定义并初始化等待队列头部*/
DECLARE_WAITQUEUE(name, tsk);/*定义等待队列元素*/
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);/*添加等待队列*/
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);/*移除等待队列*/
/* 等待事件,等待第 1 个参数queue作为等待队列头部的队列被唤醒,而且第2个参数condition必须满足*/
wait_event(queue, condition);/**/
wait_event_interruptible(queue, condition);/*可被信号打断*/
wait_event_timeout(queue, condition, timeout);/*超时退出*/
wait_event_interruptible_timeout(queue, condition, timeout);/*可超时和信号打断*/
/*唤醒队列*/
void wake_up(wait_queue_head_t *queue);/*与wait_event和wait_event_timeout配套使用*/
void wake_up_interruptible(wait_queue_head_t *queue);/*与上同理*/
/*使队列睡眠*/
sleep_on(wait_queue_head_t *q );/*于wake_up配对使用*/
interruptible_sleep_on(wait_queue_head_t *q );/*于wake_up_interruptible配对使用*/
二、linux设备驱动中并发于竞态控制
并发和竞态广泛存在,中断屏蔽、原子操作、自旋锁和互斥体都是解决并发问题的机制。中断屏蔽很少单独被
使用,原子操作只能针对整数进行,因此自旋锁和互斥体应用最为广泛。
自旋锁会导致死循环,锁定期间不允许阻塞,因此要求锁定的临界区小。互斥体允许临界区阻塞,可以适用于
临界区大的情况
1.中断屏蔽:只能禁止和使能本 CPU 内的中断,因此,并不能解决 SMP 多 CPU 引发的竞态。
local_irq_disable()/* 屏蔽中断*/
/* 临界区 */
local_irq_enable()/* 开中断 */
2.原子操作:原子操作可以保证对一个整型数据的修改是排他性的(只能针对整形).
3.自旋锁:
1)CPU 在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。
2)自旋锁可能导致系统死锁。引发这个问题最常见的情况是递归使用一个自