我们知道在字符设备驱动中,应用层调用read、write等系统调用终会调到驱动中对应的接口。 可以当应用层调用read要去读硬件的数据时,硬件的数据未准备好,那我们该怎么做?
一种办法是直接返回并报错,但是这样应用层要获得数据需要不断的调用read去访问硬件,进程的上下文在用户空间和内核空间不停的切换,耗费了CPU的资源,降低了系统效率。那么有没有更好的办法呢? 答案是有的,在这种情况下我们就可以利用Linux的阻塞机制,实现阻塞访问。
**
**
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
**
**
在 Linux 驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。wait queue 很早就作为一个基本的功能单位出现在 Linux 内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。等待队列可以用来同步对系统资源的访问,信号量在内核中也依赖等待队列来实现。
希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此,等待队列是一组睡眠的进程,当某一条件变为真时,由内核唤醒他们。
等待队列是一个具有头节点的双向循环链表,把所有睡眠的进程连接起来,每个节点元素都有进程相关的信息
init_waitqueue_head (&ql_spidev->slave_ready_wq); //将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表 原型 init_waitqueue_head (__wait_queue_head x); wake_up_interruptible(&ql_spidev->slave_ready_wq); //唤醒等待队列. 如何唤醒: 在中断进程里加入,或者别的进程里面加入 wake_up_interruptible status = wait_event_interruptible(ql_spidev->slave_ready_wq, spidev_get_slave_ready(ql_spidev)); // 休眠 所谓休眠就是让出CPU 然后并不返回 wait_event_interruptible(wq, condition) condition = 0 ///休眠 condition = 1 ///唤醒 用 wake_up_interruptible ()唤醒后,wait_event_interruptible (wq, condition)宏,自身再检查“ condition ”这个条件以决定是返回还是继续休眠,真则返回, 假则继续睡眠,不过这个程序中若有中断程序的话,中断来了,还是会继续执行中断函数的。只有当执行 wake_up_interruptible ()并且 condition 条件成立时才 会把程序从队列中唤醒。 wait_event_interruptible 是linux驱动设计中断的重要函数,他有什么用呢? 作用: 1、就是进程休眠,等待中断: 2、用在驱动里面会休眠当前的进程。 struct __wait_queue_head { spinlock_t lock; //在对 task_list 与操作的过程中,使用该锁实现对等待队列的互斥访问 struct list_head task_list; //双向循环链表,存放等待的进程。 }; typedef struct __wait_queue_head wait_queue_head_t; 等待队列介绍: 等待队列在linux内核中有着举足轻重的作用,很多linux驱动都或多或少涉及到了等待队列。 Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构: 等待队列头( wait_queue_head_t )和等待队列项( wait_queue_t )。等待队列头和等待队列项中都包含一个 list_head 类型的域作为”连接件”。 它通过一个双链表和把等待task的头,和等待的进程列表链接起来。 等待队列作用: 在内核里面,等待队列是有很多用处的,尤其是在中断处理、进程同步、定时等场合。可以使用等待队列在实现阻塞进程的唤醒。它以队列为基 础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制,同步对系统资源的访问等。