第8章 Linux设备驱动中的阻塞与非阻塞I/O-轮询操作

8.2 轮询操作

8.2.1 轮询的概念与作用

在用户程序中,select()和poll()是与设备阻塞与非阻塞访问息息相关的论题。使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问。select()和poll()系统调用使设备驱动中的poll()函数被执行。

8.2.2 应用程序中的轮询编程

一、

应用程序中最广泛用到的是BSD UNIX中引入的select()系统调用,其原型为:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的fd加1。

readfds文件集中的任何一个文件变得可读,select()返回;同理,writefds文件集中的任何一个文件变得可写,select也返回。

如图8.3所示,第一次对n个文件进行select()的时候,若任何一个文件满足要求,select()就直接返回;第2次再进行select()的时候,没有文件满足读写要求,select()的进程阻塞且睡眠。由于调用select()的时候,每个驱动的poll()接口都会被调用到,实际上执行select()的进程被挂到了每个驱动的等待队列上,可以被任何一个驱动唤醒。如果FDn变得可读写,select()返回。


图8.3 多路复用select()

timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若仍然没有文件描述符准备好则超时返回。struct timeval数据结构的定义如代码清单8.7所示。

代码清单8.7 timeval结构体定义

 struct timeval  {
        int tv_sec;     /* 秒 */
        int tv_usec;    /* 微秒 */

 };

下列操作用来设置、清除、判断文件描述符集合:

1、

FD_ZERO(fd_set *set)

清除一个文件描述符集合;

2、

FD_SET(int fd,fd_set *set)

将一个文件描述符加入文件描述符集合中;

3、

FD_CLR(int fd,fd_set *set)

将一个文件描述符从文件描述符集合中清除;

4、

FD_ISSET(int fd,fd_set *set)

判断文件描述符是否被置位。

二、

poll()的功能和实现原理与select()相似,其函数原型为:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

当多路复用的文件数量庞大、I/O流量频繁的时候,一般不太适合使用select()和poll(),这种情况下,select()和poll()的性能表现较差,宜使用epoll。epoll的最大好处是不会随着fd的数目增长而降低效率,select()则会随着fd的数量增大性能下降明显。

8.2.3 设备驱动中的轮询编程

#include <linux/fs.h>

设备驱动中poll()函数的原型是:

unsigned int(*poll)(struct file * filp, struct poll_table* wait);

第1个参数为file结构体指针,第2个参数为轮询表指针。这个函数应该进行两项工作。

1)对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头部添加到poll_table中。

2)返回表示是否能对设备进行无阻塞读、写访问的掩码。

#include <linux/poll.h>

向poll_table注册等待队列的关键poll_wait()函数的原型如下:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p);

poll_wait()函数并不会引起阻塞。poll_wait()函数所做的工作是把当前进程添加到p参数指定的等待列表(poll_table)中,实际作用是让唤醒参数wait_address对应的等待队列可以唤醒因select()而睡眠的进程。

驱动程序poll()函数应该返回设备资源的可获取状态,即POLLIN、POLLOUT、POLLPRI、POLLERR、POLLNVAL等宏的位“或”结果。每个宏的含义都表明设备的一种状态,如POLLIN(定义为0x0001)意味着设备可以无阻塞地读,POLLOUT(定义为0x0004)意味着设备可以无阻塞地写。

poll()函数典型模板

1 static unsigned int xxx_poll(struct file *filp, poll_table *wait)
 2 {
 3  unsigned int mask = 0;
 4  struct xxx_dev *dev = filp->private_data;      /* 获得设备结构体指针*/
 5
 6  ...
 7  poll_wait(filp, &dev->r_wait, wait);           /* 加入读等待队列 */
 8  poll_wait(filp, &dev->w_wait, wait);           /* 加入写等待队列 */
 9
10  if (...)                                       /* 可读 */
11      mask |= POLLIN | POLLRDNORM;               /* 标示数据可获得(对用户可读)*/
12
13  if (...)                                       /* 可写 */
14      mask |= POLLOUT | POLLWRNORM;              /* 标示数据可写入*/
15  ...
16  return mask;
17}

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80396379