当进程以阻塞的方式打开设备文件时(open默认的方式),如果资源不可用,那么进程阻塞,也就是进程进行休眠。
实际上就是如果进程发现资源不可用,那么它会主动的将资金的状态设置为TASK_UNINTERRUPTIBLE或者TASK_INTERRUPTIBLE,然后将资金加入到一个驱动所维护的等待队列中。
最后调用调度器schedule主动的释放CPU,操作系统将其从运行队列上移除,再调度其他的进行进行运行。这样做最大的好处就是,当资源不可用时,进程不占用CPU时间。
既然进程只是进行休眠,那么就应该存在唤醒的操作,否则进程将一直休眠。所以在驱动程序中,当资源可获取时,应该唤醒当前进程进行处理驱动事件。具体流程如下:
在Linux内核中,可通过等待队列在驱动程序中实现相应的阻塞操作。这就包括了进程的休眠和唤醒操作。Linux内核通过结构体struct
__wait_queue_head来描述一个等待队列,如下:
Linux内核中等待队列有如下操作:
1、定义初始化一个等待队列
2、 等待队列的基本操作
3、等待队列的interruptible操作
Interruptible表示进程在睡眠是可以通过信号来唤醒。也就是直接将当前进程设置为TASK_INTERRUPTIBLE状态,并且将其加入等待队列,然后让出调度,开始睡眠。即根据条件可以让进程进入休眠状态。
在默认情况下,唤醒操作将唤醒等待队列中所有的进程,但是如果一个进程时具有排他性的(也就是通过wait_event_interruptible_exclusive()接口加入等待队列的),那么唤醒操作在唤醒这个进程后就不会再唤醒其他进程了。
4、具有锁(locked)的等待队列操作
注:以上的所有接口的返回值如下:
函数接口不带timeout时,那么返回0表示成功唤醒,返回-ERESTARTSYS表示被信号唤醒。
函数接口带timeout时,返回0表示超时,返回大于0的值表示被成功唤醒,并且这个值表示离超时还剩余的时间。
通过以上介绍了关于等待队列实现阻塞的机制和函数接口,那么如何使用等待队列实现阻塞机制呢?
1、定义一个等待队列头,并将其初始化。
示例:
wait_queue_head_t wait_queue;
init_waitqueue_head(wait_queue)
2.定义一个状态标识,标识获得资源是否可用。
3.在struct file_operations函数操作集中与应用程序对应的接口(例如read())实现阻塞。
4.在中断中或者获取资源的接口中唤醒进程并标识当前资源可用。笔者在此通过按键中断的驱动程序来实现read()接口的阻塞操作。
头文件部分代码:waitqueue_driver.h
下面是源代码:waitqueue_driver.c
以下定义全局结构对象:
中断下半部处理程序:
中断上半部程序:
操作函数集接口:
操作函数集与硬件初始化:
驱动退出函数:
通过以上程序可实现按键的阻塞机制,在应用程序中,使用read即可读取应用程序状态,当没有按键按下时,Buttons_Method->key_state的取值为0,此时资源不可用。
在驱动button__read函数接口中的wait_event_interruptible(Buttons_Method->buttons_wq,Buttons_Method->key_state)处阻塞;
当按键按下时。Buttons_Method->key_state的值为非0,此时资源可用,进程被唤醒,跳出阻塞,然后将按键键值通过copy_to_user(buf,&event,count);传输到应用空间中,最后应用程序中的read读取到键值。应用程序代码如下:
总结:本文通过实例进行分析了通过等待队列wait queue在Linux内核驱动中实现阻塞机制,并详细分析了阻塞的原理和接口。
更多Linux教程请查看菜单