POLL方法
什么是POLL方法,功能是什么?
系统调用(用户空间) | 驱动(内核空间) |
Open | Open |
Close | Release |
Read | Read |
Write | Write |
Ioctl | ioctl |
Lseek | Llseek |
Select | poll |
Select系统调用(功能)
Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程
Int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)
Maxfd | 文件描述符的范围,比待检测的最大文件描述符大1 |
Readfds | 被读监控的文件描述符集 |
Writefds | 被写监控的文件描述符集 |
Exceptfds | 被异常监控的文件描述符集 |
Timeout | 定时器 |
Timeout 取不同的值,该调用有不同的表现
1)Timeout为0,不管是否有文件满足要求,都立即返回,无文件满足要求返回0,有文件满足要求返回一个正值。
2)Timeout 为null,select将阻塞进程,直到某个文件满足要求。
3)Timeout 值为正整数,就是等待的最长时间,即select在timeout 时间内阻塞进程。
Select调用返回时,返回值有如下情况:
1)正常情况下返回满足要求的文件描述符个数
2)经过了timeout等待后仍无文件满足要求,返回值为0
3)如果select被某个信号中断,它将返回-1并设置errno为EINTR
4)如果出错,返回-1并设置相应的errno
Select系统调用(使用方法)
1)将要监控的文件添加到文件描述符集
2)调用select开始监控
3)判断文件是否发生变化
系统提供了4个宏对描述符集进行操作:
#include<sys/select.h>
Void FD_SET(int fd, fd_set *fdset)
Void FD_CLR(int fd, fd_set *fdset)
Void FD_ZERO(fd_set *fdset)
Void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET将文件描述符fd添加到文件描述符fdset中
宏FD_CLR从文件描述符集fdset中清除文件描述符fd
宏FD_ZERO清空文件描述符集fdset
在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化
使用例子(对两个文件进行读监控):
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方法
原型:unsigned int (*poll)(struct file *filp, poll_table *wait)
负责完成:
1)使用poll_wait将等待队列添加到poll_table 中
2)返回描述设备是否可读或者可写的掩码
位掩码:
POLLIN
设备可读
POLLRDNORM
数据可读
POLLOUT
设备可写
POLLWRNORM
设备可写
例子:
Static unsigned int mem_poll(struct file *filp, poll_table *wait)
{
Struct scull_pipe *dev = filp->private_data;
Unsigned int mask = 0;
Poll_wait(filp,&dev->inq,wait); //把等待队列添加到poll-table
If(有数据可读)
Mask = PONNIN | POLLRDNORM; //设备可读
Return mask; //返回掩码
}
工作原理:
POLL方法只是做一个登记,真正的阻塞发生在select.c中的do_select函数
内核代码分析:
do_select是select系统调用所对应的内核函数,do_select完成select的功能。
int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
ktime_t expire, *to = NULL;
struct poll_wqueues table;
poll_table *wait;
int retval, i, timed_out = 0;
unsigned long slack = 0;
rcu_read_lock();
retval = max_select_fd(n, fds);
rcu_read_unlock();
if (retval < 0)
return retval;
n = retval;
poll_initwait(&table);
wait = &table.pt;
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
wait = NULL;
timed_out = 1;
}
if (end_time && !timed_out)
slack = estimate_accuracy(end_time);
retval = 0;
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
inp = fds->in; outp = fds->out; exp = fds->ex;
rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
const struct file_operations *f_op = NULL;
struct file *file = NULL;
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
if (all_bits == 0) {
i += __NFDBITS;
continue;
}
for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
int fput_needed;
if (i >= n)
break;
if (!(bit & all_bits))
continue;
file = fget_light(i, &fput_needed); //把file拿到,但不懂fget_light()
if (file) {
f_op = file->f_op;//拿到方法指针
mask = DEFAULT_POLLMASK;
if (f_op && f_op->poll) {
wait_key_set(wait, in, out, bit);
mask = (*f_op->poll)(file, wait);//调用poll,把掩码拿到
}
fput_light(file, fput_needed);
if ((mask & POLLIN_SET) && (in & bit)) {//判断是否在读监控集合里
res_in |= bit; //POLLIN_SET是一个集合
retval++; //判断文件是否可读
wait = NULL;
}
if ((mask & POLLOUT_SET) && (out & bit)) {//输出
res_out |= bit;
retval++;
wait = NULL;
}
if ((mask & POLLEX_SET) && (ex & bit)) {//异常
res_ex |= bit;
retval++;
wait = NULL;
}
}
}
if (res_in)
*rinp = res_in;
if (res_out)
*routp = res_out;
if (res_ex)
*rexp = res_ex;
cond_resched();
}
wait = NULL;
if (retval || timed_out || signal_pending(current))//判断超时、有信号、有文件满足?跳出大循环
break;
if (table.error) {
retval = table.error;
break;
}
/*
* If this is the first loop and we have a timeout
* given, then we convert to ktime_t and set the to
* pointer to the expiry value.
*/
if (end_time && !to) {
expire = timespec_to_ktime(*end_time);
to = &expire;
}
if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,//带有限时的调度,进入TASK_INTERRUPTIBLE的睡眠,相当于阻塞了
to, slack))
timed_out = 1;
}
poll_freewait(&table);
return retval;
}