linux设备驱动中的I/O
阻塞于非阻塞I/O
阻塞I/O与非阻塞I/O是两种I/O访存的方式,一种当I/O资源不可用时,会阻塞进程执行,并放弃CPU资源;另一种当I/O资源不可用时会直接返回一个失败值。对这两种I/O方式都有其各自不同的应用场景,相对于非阻塞I/O来说,阻塞I/O在内核中的实现更为复杂。
阻塞I/O的实现依赖于等待队列机制,等待队列机制也是信号量机制实现的基础,它可以让依赖某事件的阻塞进程睡眠等待,直到适当的时机被唤醒。
等待队列
linux提供了等待队列的实现及接口:
1 | wait_queue_head_t wq; |
如下为等待队列使用的一个示例:
1 | static ssize_t xxx_write(struct file *filp, const char *buffer, size_t count, loff_t *ppos) |
阻塞与非阻塞I/O的实现与进程状态密切相关,因此在做相应开发时,需要深刻理解进程状态的切换。同时通过下图可以了解进程与等待队列之间的关系。
轮询操作
一般I/O操作能够满足我们通常的需求,但随着计算机技术和市场需求的发展,特别是高并发网络服务器的需求,对于I/O操作的多路复用问题成为一个不得不考虑的问题。在这样的背景下,select、poll、epoll等这样的事件模型应运而生。
linux中提供了select,poll这样的系统调用,使得对多个设备进行无阻塞的访问变得更加方便,效率也更高。这为用户空间的I/O编程提供了更统一的策略。但这一切都是建立在内核的相应实现上的。
我们要对一个设备进行select、poll的操作,需要在相应设备驱动中实现如下接口:
1 | unsigned int (*poll)(struct file *filp, struct poll_table *wait); |
其中filp是相应的文件指针,而wait则是轮询表指针。可以使用poll_wait接口将可能引起设备状态变化的等待队列头部添加到轮询表中。其定义如下:
1 | void poll_wait(struct file *filp, wait_queue_head_t *queue, struct poll_table *wait); |
而实现的poll接口返回值则为设备资源的可获取状态,如POLLIN、POLLOUT、POLLPRI、POLLERR、POLLNVAL的‘或’结果。
因此驱动中应实现的poll接口应如下所示:
1 | static unsigned int xxx_poll(struct file *filp, struct poll_table *wait) |
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com
文章标题:linux设备驱动中的I/O
本文作者:红尘追风
发布时间:2016-06-23, 18:55:30
原始链接:http://www.micernel.com/2016/06/23/linux%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E7%9A%84I_O/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。