关于poll函数具体是干什么的,以及什么情况下使用等参考我的其他博客
常见系统调用一般对应内核中sys_函数名,比如我们想看poll机制,具体怎么查看源码呢?
notes:
很多其他博客都说poll调用的是内核函数sys_poll,结果在现在较新linux版本源码select.c中却找不到,较新linux版本中采用宏组合的方式来表示sys_poll,在select.c中,
我们应该查看的是SYSCALL_DEFINE3
/*
下列函数主要做了三件事
*/
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
long, timeout_msecs)
{
struct timespec end_time, *to = NULL;
int ret;
if (timeout_msecs >= 0) {
to = &end_time;
poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC, //是一个时间转换函数,根据传入的时间参数计算超时时间
//存放入一个 struct timespec结构体实例to中
NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
}
ret = do_sys_poll(ufds, nfds, to); //调用do_sys_poll完成主要工作(实现轮询功能)
if (ret == -EINTR) { //do_sys_poll被信号中断的处理
struct restart_block *restart_block;
restart_block = ¤t_thread_info()->restart_block;
restart_block->fn = do_restart_poll;
restart_block->poll.ufds = ufds;
restart_block->poll.nfds = nfds;
if (timeout_msecs >= 0) {
restart_block->poll.tv_sec = end_time.tv_sec;
restart_block->poll.tv_nsec = end_time.tv_nsec;
restart_block->poll.has_timeout = 1;
} else
restart_block->poll.has_timeout = 0;
ret = -ERESTART_RESTARTBLOCK;
}
return ret;
}
linux/poll.h
struct poll_wqueues {
poll_table pt;
struct poll_table_page *table;
struct task_struct *polling_task;
int triggered;
int error;
int inline_index;
struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
};
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
typedef struct poll_table_struct {
poll_queue_proc qproc;
} poll_table; //其中就只有一个函数指针成员
void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait); //设置poll_table结构中的qproc函数指针为__pollwait函数,
// 就是pwq->pt->qproc=__pollwait。这个函数是一个回调函数,基本上这种机制的实现,就是依靠回调函数了,用于存储回调函数的指针
pwq->polling_task = current; //调用poll_initwait时,其中的polling_task成员被赋值为当前进程的task_struct,也即current
pwq->triggered = 0;
pwq->error = 0;
pwq->table = NULL;
pwq->inline_index = 0;
}
创建一个struct poll_wqueues类型的挑选队列,并由poll_initwait初始化,接着调用do_poll进入循环遍历poll_list的操作
__pollwait
fdcount = do_poll(nfds, head, &table, end_time); //循环遍历poll_list链表,检测每个节点中的存储fd的数组,
其中参数nfds为用户传入的整数,代表传入的pollfd的数量,而head即为拷贝后的poll_list链表,wait是挑选队列,而end_time就是超时时间。
do_poll对poll_list链表进行循环处理,对于单个fd,则调用do_pollfd进行处理。另外注意到在一次遍历之后一旦返现do_pollfd的返回值不为0,
则说明该描述符可操作,计入count,如果count不为0或者超时则直接跳出循环,并返回活跃描述符的计数。
*/
static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, struct timespec *end_time)
{
poll_table* pt = &wait->pt;
ktime_t expire, *to = NULL;
int timed_out = 0, count = 0;
unsigned long slack = 0;
/* Optimise the no-wait case */
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
pt = NULL;
timed_out = 1;
}
if (end_time && !timed_out)
slack = estimate_accuracy(end_time);
for (;;) {
struct poll_list *walk;
for (walk = list; walk != NULL; walk = walk->next) { //循环遍历poll_list链表
struct pollfd * pfd, * pfd_end;
pfd = walk->entries;
pfd_end = pfd + walk->len;
for (; pfd != pfd_end; pfd++) {
/*
* Fish for events. If we found one, record it
* and kill the poll_table, so we don't
* needlessly register any other waiters after
* this. They'll get immediately deregistered
* when we break out and return.
*/
if (do_pollfd(pfd, pt)) { //对于每个fd,都调用do_pollfd函数,如果返回值不为0,则说明描述符准备就绪,可操作,count加1
count++;
pt = NULL;
}
}
}
/*
* All waiters have already been registered, so don't provide
* a poll_table to them on the next loop iteration.
*/
pt = NULL;
if (!count) {
count = wait->error;
if (signal_pending(current))
count = -EINTR;
}
if (count || timed_out)
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(wait, TASK_INTERRUPTIBLE, to, slack))
timed_out = 1;
}
return count;
}
以下这段总结摘自: http://blog.csdn.net/zmxiangde_88/article/details/8099049
这个函数有以下几个要注意的点:
/* * Fish for pollable events on the pollfd->fd file descriptor. We're only * interested in events matching the pollfd->events mask, and the result * matching that mask is both recorded in pollfd->revents and returned. The * pwait poll_table will be used by the fd-provided poll handler for waiting, * if non-NULL. */
/*
do_pollfd调用驱动提供的poll函数,如果没有则永远返回0。poll 返回位掩码, 它描述哪个操作可马上被实现;
例如, 如果设备有数据可用, 一个读可能不必睡眠而完成; poll 方法应当指示这个时间状态。
*/
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait){ unsigned int mask; int fd; mask = 0; fd = pollfd->fd; if (fd >= 0) { int fput_needed; struct file * file; file = fget_light(fd, &fput_needed); //根据 fd 指定的索引,从当前进程描述符中取出相应的 file 对象 mask = POLLNVAL; if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) {
/*调用file->f_op->poll(file,pwait),这是这个函数的核心调用,这其实也是linux的VFS的一部分,这会根据当前的文件是什么类型的文件来选择调用的入口,
如file是socket网络文件,此时调用的就是由网络驱动设备来实现的poll,如果file是ext3等文件系统上打开的一个文件,那就会调用由该文件系统来实现的poll函数*/ if (pwait) pwait->key = pollfd->events | POLLERR | POLLHUP; mask = file->f_op->poll(file, pwait); } /* Mask out unneeded events. */ mask &= pollfd->events | POLLERR | POLLHUP; fput_light(file, fput_needed); } } pollfd->revents = mask; //可以看出pollfd中的revents最后其实是被do_pollfd修改 return mask;}
//注明:以下摘自http://blog.csdn.net/zmxiangde_88/article/details/8099049
那么各种类型的驱动poll函数机制是怎么样的呢?
后续更新