转载声明: https://www.cnblogs.com/hanyan225/archive/2010/10/13/1850497.html
使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问,这两个系统调用最终又会引发设备驱动中的poll()函数被执行
,所以我们的问题就集中到了如何编写设备驱动中的poll()函数就可以了。二话不说,先来看看设备驱动中的poll()函数原型:
unsigned int (*poll)(struct file *filp, struct poll_table *wait);
这个函数要进行下面两项工作。首先,对可能引起设备文件状态变化的等待队列调用poll_wait(),将对应的等待队列头添加到poll_table.然后,返回表示是否能对设备进行无阻塞读写访问的掩码。在上面提到了一个poll_wait()函数,它的原型:
void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);
它的作用就是把当前进程添加到wait参数指定的等待列表(poll_table)中。需要注意的是这个函数是不会引起阻塞的。
static unsigned int XXX_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
struct XXX_dev *dev = filp->private_data; //获得设备结构指针
...
poll_wait(filp, &dev->r_wait, wait); //加读等待对列头
poll_wait(filp ,&dev->w_wait, wait); //加写等待队列头
if(...)//可读
{
mask |= POLLIN | POLLRDNORM; //标识数据可获得
}
if(...)//可写
{
mask |= POLLOUT | POLLRDNORM; //标识数据可写入
}
..
return mask;
}
在用户程序中,select()和poll()本质上是一样的, 不同只是引入的方式不同,前者是在BSD UNIX中引入的,后者是在System V中引入的。用的比较广泛的是select
系统调用。原型如下:
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptionfds, struct timeval *timeout);
其中readfs,writefds,exceptfds分别是select()监视的读,写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1,timeout则是一个时间上限值,超过该值后,即使仍没有描述符准备好也会返回。
涉及到文件描述符集合的操作主要有以下几种:
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);
最后我们利用上面的文件描述符集的相关来写个验证添加了设备轮询的驱动,把上边两块联系起来:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20
main()
{
int fd, num;
char rd_ch[BUFFER_LEN];
fd_set rfds,wfds;
/*以非阻塞方式打开/dev/polltest设备文件*/
fd = open("/dev/polltest", O_RDONLY | O_NONBLOCK);
if (fd != - 1)
{ /*FIFO清0*/
if (ioctl(fd, FIFO_CLEAR, 0) < 0)
{
printf("ioctl command failed\n");
}
while (1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd, &rfds);
FD_SET(fd, &wfds);
select(fd + 1, &rfds, &wfds, NULL, NULL);
/*数据可获得*/
if (FD_ISSET(fd, &rfds))
{
printf("Device can be read now\n");
}
/*数据可写入*/
if (FD_ISSET(fd, &wfds))
{
printf("Device can be written now\n");
}
}
}
else
{
printf("Device open failure now\n");
}
}
其中需要注意,select将更新rfds和wfds集合;