linux设备驱动开发详解--第八章 Linux 设备驱动中的阻塞与非阻塞 I/O

本文档参考着linux设备驱动开发详解一起看


8.1 阻塞与非阻塞 I/O

阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作;被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。


         阻塞从字面上听起来似乎意味着低效率,实则不然,如果设备驱动不阻塞,则用户想获取设备资源只能不停地查询,这反而会无谓地耗费 CPU 资源。而阻塞访问时,不能获取资源的进程将进入休眠,它将 CPU 资源让给其他进程。

        因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。

因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。

8.1.1等待队列

在 Linux 驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。等待队列可以用来同步对系统资源的访问,信号量在内核中也依赖等待队列来实现

Linux 2.6 提供如下关于等待队列的操作:

1.定义“等待队列头”

wait_queue_head_t my_queue;

2.初始化“等待队列头

init_waitqueue_head(&my_queue);
而下面的 DECLARE_WAIT_QUEUE_HEAD()宏可以作为定义并初始化等待队列头的“快捷方式”
DECLARE_WAIT_QUEUE_HEAD (name)

3.定义等待队列

DECLARE_WAIT_QUEUE_HEAD (name)
该宏用于定义并初始化一个名为 name 的等待队列

4.添加/移除等待队列

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 指向的等待队而列链表中移除

5.等待事件

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 是否满足,均返回。wait()的定义如代码清单 8.3 所示,从其源代码可以看出,当 condition 满足时,wait_event()会立即返回,否则,阻塞等待 condition 满足。

6.唤醒队列

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 的进程。

7.在等待队列上睡眠
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()成对使用。

在内核中使用 set_current_state()函数或_ _add_wait_queue()函数来实现目前进程状态的改变,直接采用 current->state = TASK_UNINTERRUPTIBLE 类似的赋值语句也
是可行的。通常而言,set_current_state()函数在任何环境下都可以使用,不会存在并发问题,但是效率要低于_ _add_ wait_queue()。因此,在许多设备驱动中,并不调用 sleep_on()或 interruptible_sleep_on(),而是亲自进行进程的状态改变和切换,


8.1.2支持阻塞操作的 globalfifo 设备驱动



























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值