深入理解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,并且如何实现阻塞,如何实现唤醒
先到这里,改天继续。。。