深入理解linux内核poll机制

2019-07-12 23:29发布

深入理解linux内核poll机制
前言:以下内容,部分参考网络资料
poll机制实现的是多路io转接,简单讲就是多个文件进行监控,对应到应用层是select函数

#include int select (int maxfdp1, fd_set *restict readfds, fd_set *restrict writefds,/ fd_set *restrict expectfds, struct timeval *restrict tvptr);
参数:
maxfdp1:文件描述符的范围,比待检测的最大文件描述符大1
readfds:被读监控的文件描述符集
writefds:被写监控的文件描述符集
expectfds:被异常监控的文件描述符集
tvptr:愿意等待的时间
struct timeval {
long tv_sec;
long tv_usec;
};
等待时间可取的值有三种:
1. tvptr = 0; 等待时间为0,无论文件是否满足要求,都会立即返回
2. tvptr = NULL; 等待时间为无限长,一直到有某个文件满足要求,返回一个正数
3. tvptr = time; time为正数,等待时间time到了以后,若没有文件满足要求,也要返回

select函数返回值:
1.返回一个正数,表示满足要求的文件描述符的个数
2.返回0,表示时间到了任然没有满足条件的文件描述符
3.返回-1,被某个信号中断,或者是出错

文件描述符集的概念
readfds、writefds和expectfds是指向描述符集的指针。这三个描述符集说明了我们关系的可读、可写或处于异常
条件的各个描述符。每个描述符集放在一个fd_set数据类型中。
每个被监控的文件描述符占fd_set数据类型的一位,如图

系统提供4个宏对文件描述符进行操作:
1. void FD_SET(int fd, fd_set *fdset)
将文件描述符添加到文件描述符集fd_set中
2. void FD_CLR(int fd, fd_set *fdset)
将文件描述符从fd_set中清除
3. void FD_ZERO(fd_set *fdset)
将文件描述符集fdset清空
3. void FD_ISSET(int fd, fd_set *fdset)
select函数返回后判断某个文件fd是否发生变化

应用层select函数使用方法:
fd_set fds; int fd1,fd2; FD_ZERO(&fds); //清空集合 FD_SET(fd1,&fds); //设置描述符 FD_SET(fd2,&fds); //设置描述符 maxfdp=fd1+1; //描述符最大值加1,假设fd1>fd2 switch(select(maxfdp,&fds,NULL,NULL,&timeout)) { case -1: exit(-1);break; //select错误,退出程序 case 0: break; default: if(FD_ISSET(fd1,&fds)){ //... } //测试fd1是否可读 }
==============================================================================
下面介绍一下内核空间,驱动中poll函数的实现
这个poll函数不是应用层的poll函数,而是在file_operations中的poll函数
以下内核代码基于 linux 3.8 内核

unsigned int (*poll)(struct file *filp, poll_table *wait)
参数:
struct file *filp : 驱动的设备文件符
poll_table *wait : 用来初始化等待队列,后面将分析

该函数的作用有两个:
一是将等待队列添加到poll_table中,第二个是返回可读可写的掩码
    POLLIN         有数据可读。
  POLLRDNORM       有普通数据可读。
  POLLRDBAND      有优先数据可读。
  POLLPRI         有紧迫数据可读。
  POLLOUT            写数据不会导致阻塞。
  POLLWRNORM       写普通数据不会导致阻塞。
  POLLWRBAND        写优先数据不会导致阻塞。
  POLLMSGSIGPOLL     消息可用。
  此外,revents域中还可能返回下列事件:
  POLLER     指定的文件描述符发生错误。
  POLLHUP   指定的文件描述符挂起事件。
   POLLNVAL  指定的文件描述符非法。
设备可读通常返回(POLLIN|POLLRDNORM)
设备可写通常返回(POLLOUT|POLLWRNORM)

poll函数编写框架:
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,并且如何实现阻塞,如何实现唤醒 先到这里,改天继续。。。