应用层通过系统调用poll函数进入内核,内核执行相应的sys_poll函数。在sys_poll函数中调用do_sys_poll函数。do_sys_poll函数通过调用poll_initwait函数初始化poll_wqueues变量table,并且将__pollwait函数赋值给table。这里的__pollwait函数将在驱动的poll方法中通过调用poll_wait函数来执行。poll_wait会执行p->_qproc,也就是__pollwait,将当前task加入等待队列后阻塞住
poll_initwait函数执行完后,do_sys_poll函数会调用do_poll函数。
在do_poll函数中有一个for的死循环,退出条件为count或者timeout为非零。
count为非零标识do_pollfd函数返回的mask为真,timeout表示定时时间到。
在do_pollfd函数中通过mask = file->f_op->poll来调用驱动中的poll方法,并且获得驱动中操作poll后的mask值。
在for死循环中,首先轮询整条poll_list。在所有等待poll的操作中寻找是否有已满足条件的操作,有则跳出循环
没有则继续向下执行。
函数调用流程: SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, int, timeout_msecs) //也就是sys_poll -> do_sys_poll -> poll_initwait //初始化poll_wqueues,设置poll_table的处理函数qproc为__pollwait,这里的__pollwait函数将在驱动的poll方法中通过调用poll_wait函数来执行。,poll_wait会执行p->_qproc,也就是__pollwait,将当前task加入等待队列后阻塞住 -> do_poll -> do_pollfd(死循环等待,直到条件满足发生退出,返回sys_poll后,返回用户态) |
总结下poll的机制:
1.poll->sys_poll->do_sys_poll->poll_initwait
* 初始化poll_wqueues,设置poll_table的处理函数qproc为__pollwait,
* 这里的__pollwait函数将在驱动的poll方法中通过调用poll_wait函数来执行。
* poll_wait会执行p->_qproc,也就是__pollwait,将当前task加入等待队列后阻塞住
*/
{
init_poll_funcptr(&pwq->pt, __pollwait);
pwq->polling_task = current;
pwq->triggered = 0;
pwq->error = 0;
pwq->table = NULL;
pwq->inline_index = 0;
}
此处需注意init_poll_funcptr(&pwq->pt, __pollwait),将函数__pollwait函数赋值给&pwq->pt指向的pooll_table结构中的函数指针_proc。
/*
* 通过调用poll_table注册的_qproc函数,来实现将任务加入等待队列,select中为__pollwait
*/
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && p->_qproc && wait_address)
p->_qproc(filp, wait_address, p);
}
此处p->qproc操作指向的就是__pollwait函数
static int do_poll(unsigned int nfds, struct poll_list *list, struct poll_wqueues *wait, struct timespec *end_time)
{
....
/*
* 在for死循环中,首先轮询整条poll_list。在所有等待poll的操作中寻找是否有已满足条件的操作,有则跳出循环
*/
for (;;) {
for (walk = list; walk != NULL; walk = walk->next) {
for (; pfd != pfd_end; pfd++) {
/*
* 在do_pollfd函数中通过mask = file->f_op->poll来调用驱动中的poll方法,并且获得驱动中操作poll后的mask值。
*/
if (do_pollfd(pfd, pt, &can_busy_loop, busy_flag)) {
count++;
pt->_qproc = NULL;
/* found something, stop busy polling */
busy_flag = 0;
can_busy_loop = false;
}
}
}
if (count || timed_out)
break;
timed_out = 1;
}
return count;
}
在poll_initwait函数初始化完毕后,do_sys_poll函数会调用do_poll函数。
if (fd >= 0) {
if (f.file) {
/*
* 即调用驱动中的poll函数(在写驱动程序时编写poll函数)
*/
mask = f.file->f_op->poll(f.file, pwait);
}
}
return mask;
}
通过file->f_op->poll来调用驱动中的poll方法,而驱动中的poll方法会调用poll_wait函数
poll_wait函数调用poll_initwait函数注册的__pollwait函数(驱动函数的poll的实现方法中,一般都会调用这个poll_wait,如dvb_audio_poll(), bttv_poll())
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
{
struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
struct poll_table_entry *entry = poll_get_entry(pwq);
if (!entry)
return;
entry->filp = get_file(filp);
entry->wait_address = wait_address;
entry->key = p->_key;
init_waitqueue_func_entry(&entry->wait, pollwake);
entry->wait.private = pwq;
/*将当前实体加入到等待队列中*/
add_wait_queue(wait_address, &entry->wait);
}
可以发现此函数就是完成添加队列的工作。
然后继续向下执行poll_schedule_timeout()
执行完此函数后,进程进入休眠,直到被wake_up或者休眠时间到。
所以总结poll的操作:首先调用poll函数将__pollwait函数注册到系统,然后通过f_ops->poll调用驱动的poll方法,通过poll->poll_wait来调用之前注册的__pollwait函数,在__pollwait函数中添加等待队列,并调用poll_schedule_timeout()函数将其休眠,等待其他线程唤醒。所以使用polll和使用等待队列进行简单休眠一样需要在其他地方使用wake_up函数来通知
3.等待队列的休眠
驱动中调用wait_event可以将函数置入休眠,看下具体操作
do { \
if (condition) \
break; \
__wait_event(wq, condition); \
} while (0)
* 通过__wait_event中的for(;;)循环后,队列进入休眠。休眠后等待wake_up
* 每次通过schedule()进行休眠
*/
#define __wait_event(wq, condition) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
schedule(); \
} \
finish_wait(&wq, &__wait); \
} while (0)
通过__wait_event中的for(;;)循环后,队列进入休眠。休眠后等待wake_up