概述
应用程序通过read/write等系统接口对设备进行操作而不关心设备是否准备好。如果应用程序在对设备进行操作时设备未准备好,就必须在驱动程序中进行处理。
在打开一个设备文件时,可以指定打开方式为阻塞或者非阻塞型的。这两种不同的方式决定了再驱动程序中如何处理设备未准备好时的情况。
在通过open打开一个(设备)文件时通过是否设置O_NONBLOCK位来决定是以哪种方式打开文件的:
/*以只读非阻塞的方式打开文件,若未设置O_NONBLOCK则默认是阻塞的*/
fd = open(file_name, O_RDONLY|O_NONBLOCK);
如果应用程序指定的是以非阻塞的方式打开设备文件,当设备未准备好时在驱动程序中直接返回即可。通常我们可以返回-EAGAIN。
如果应用程序指定的是以阻塞的方式打开设备文件,在设备未做好准备时阻塞应用程序。此处,阻塞的含义是让应用程序的当前进程进入休眠状态,当设备准备好时再将其唤醒。
在驱动程序中休眠唤醒进程的一种方式是使用等待队列。内核提供了一些使用等待队列的常用接口:wait_queue_head_t my_wait_queue; //声明一个等待队列头
init_waitqueue_head(&my_wait_queue); //初始化等待队列头
/* 上面是采用动态的方式定义等待队列头
* 也可以用下面的方式来静态定义一个等待队列头
*/
DECLARE_WAIT_QUEUE_HEAD(my_wait_queue); //这个宏帮我们完成了初始化的动作
经过上面的操作,即获得了一个等待队列头。如果应用程序是以阻塞的方式读取设备文件,只需在驱动程序的read回调函数中加入下面的接口:
wait_event(my_wait_queue, condition);
当驱动程序运行到这行代码时,会判断condition是否为真,如果为真则驱动程序的代码会继续向下运行。否则,会将当前进程置于休眠状态(及阻塞了应用程序)。
有地方让进程休眠,必然有地方唤醒进程。通常唤醒进程的地方是能让condition为真的地方,如某些设备驱动程序的中断处理例程中。如果看之前的memdev驱动程序的话,没有用到中断。针对mem设备,读取时未做好准备的含义明显是这段内存中没有数据,我们可以在write函数中通过写入数据来结束这种状态。可以讲进程的唤醒工作放在write中,将进程唤醒的接口是:
wake_up(&my_wait_queue);
运用上面的几个接口,我们就可以完成一些简单的休眠唤醒工作了。当然除了上面的接口,内核提供了一些功能更加丰富的等待队列接口:
/*休眠进程的接口*/
wait_event_interruptible(my_wait_queue, condition);
wait_event_timeout(my_wait_queue, condition, timeout); //timeout是jiffy表示的
wait_event_interruptible_timeout(my_wait_queue, condition, timeout);
/*唤醒进程的接口*/
wake_up_interruptible(&my_wait_queue);
不带interruptible的休眠唤醒函数,当时进程休眠后无法被中断。通常使用带interruptible版本的休眠唤醒函数。
下面就已之前的memdev驱动程序为例来系统看一下这些接口该如何使用。大致的思路是在读取内存中的数据前先确定内存中是否有数据,如果没有数据则当读取的进程休眠。直至另一个向内存中写入数据的进程写入数据之后,再将休眠的进程唤醒。
首先,在设备结构体中增加等待队列和是否可读标识位两个成员,并在加载模块的时候对他们进行初始化:
struct memdev {
…
wait_queue_head_t my_wait_queue;
int readable_num;
} *devp;
static int __init memdev_init(void)
{
…
readable_num = 0;
init_waitqueue_head(&devp->my_wait_queue);
…
}
然后在驱动程序的read函数加入休眠接口:
size_t memdev_read(…)
{ …
/*判断是否有可读数据,如果没有则休眠进程*/
wait_event_interruptible(devp->my_wait_queue, devp->readable_num > 0);
…
/*读取数据的数目*/
size = size < devp->readable_num?size: devp->readable_num;
…
/*update可读数据的数目*/
devp->readable_num -= size;
…
}
最后在驱动程序的write函数中加入唤醒接口:
size_t memdev_write(…)
{ …
/* update可读数据的数目*/
devp->readable_num += size;
/*唤醒休眠的进程*/
wake_up_interruptible(&devp->my_wait_queue);
…
}
最后
以上就是繁荣酒窝为你收集整理的Linux设备驱动程序学习笔记09:等待队列的全部内容,希望文章能够帮你解决Linux设备驱动程序学习笔记09:等待队列所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复