一、poll介绍
在使用非阻塞IO中,也常常使用poll、select、epoll系统调用。三者本质上一致:允许进程决定是否可以对一个或者多个打开的文件做非阻塞的读取或者写入。当使用poll(select、epoo样,下面以poll为例)时,会进行阻塞,知道打开给定的文件集有可以进行读取、写入。常常用于需要多个输入或者输出流而又不会阻塞于其中任何一个流的应用程序中。在应用程序中调用poll系统调用,会将所有给定的文件集进行监控,直到有能用的或者超时返回,poll系统调用原型如下:
int poll(struct pollfd *fdarray,unsigned long nfds,int timeout);
其中struct pollfd为:
struct pollfd{
int fd; //需要检测的文件描述符
short events; //请求的事件
short revents; //返回的事件
};
在使用中,fdarray是一个数组,里面记载了所有要监测的文件信息。nfds表示需要监测多少个文件,timeout表示超过多少ms返回。有文件能用时,返回值表示其数目,。超时返回0,错误返回-1。
在events、revents中,events表示poll要监测的事件,revents是文件返回的事件,这些事件包括:
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通或优先级带数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
此外,revents有三个事件,表示文件出现问题
POLLERR | 发生错误 |
POLLHUP | 发生挂起 |
POLLNVAL | 描述字不是一个打开的文件 |
通常,驱动中并不使用POLLRDBAND、POLLWRBAND,这两个只在与套接字有关的文件描述符中才有意义。
二、驱动中的poll
pol1、select、epoll三者类似,这里我们只关注驱动部分的poll,驱动中,poll的原型为:
unsigned int (*poll)(struct file *filep,poll_table *wait);
驱动中使用poll很简单,以按键为例。
static unsigned int key_poll_poll (struct file * fp, poll_table * wait)
{
unsigned int mask = 0;
poll_wait(fp,&key_poll_drv_queue, wait);
if(key_flag == 1){
mask |= POLLIN ;
}
else if(key_flag == 2){
mask |= POLLOUT;
}
return mask;
}
key_poll_drv_queue是等待队列,已经定义并初始化:
DECLARE_WAIT_QUEUE_HEAD(key_poll_drv_queue);
key_flag等于1或2是测试用,实际则是表示程序有结果(POLLIN或者POLLOUT),可以返回,key_flag等于0表示没有操作,mask返回0。在驱动中,poll会使用等待队列,将调用的进程进行阻塞,进行休眠,实现查询事件。本例中,按键按下,会触发中断,在中断中改变key_flag的值,则返回mask值。驱动中实现起来还是比较简单的。
三、测试
编写app进行测试,首先打开文件,之后配置struct pollfd结构体,调用poll等待返回,根据返回值判断。
代码如下:
char* file_path = "/dev/key_htq";
int main(void)
{
struct pollfd fds[5];
int rc = 0;
int i = 0;
int fd = open(file_path, O_RDWR);
fds[0].fd = fd;
fds[0].events = POLLIN | POLLOUT;
printf("POLLIN: %d\n",POLLIN);
printf("POLLOUT: %d\n",POLLOUT);
while(1){
rc = poll(fds, 5, 3 * 1000);
printf("rc: %d\n",rc);
for(i = 0;i < 5;i++){
if ((fds[i].revents == POLLIN) || (fds[i].revents == POLLOUT))
{
printf("i: %d revents: %d\n",i,fds[i].revents);
}
}
sleep(1);
}
}
一共5个文件(实际就监控1个文件,按键),返回之后,去便利所有的监控文件,
找到有返回的文件,同时,还可以根据revents具体查找需要操作的文件。
测试过程如下:
首先,加载驱动
进行测试,
可以看到,POLLIN的值是1,POLLOUT的值是4,按下按键之后,revents有不同的返回值,分别对应POLLIN(key_flag等于1)、POLLOUT(key_flag等于2)、rc返回为0(key_flag等于0)。结果表明按键的poll驱动运行成功。