POLL机制的内核代码详解
Linux APP 系统调用,基本都可以在它的名字前加上 sys_
前缀,这就是它在内核中对应的函数。比如系统调用 open
、read
、write
、poll
,与之对应的内核函数为 sys_open
、sys_read
、sys_write
、sys_poll
。
对于系统调用 poll
和 select
,他们对应的内核函数都是 sys_poll
。分析 sys_poll
,即可理解 poll
机制。
sys_poll 函数
sys_poll 位于 fs/select.c 文件中,代码如下:
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
int, timeout_msecs)
{
struct timespec64 end_time, *to = NULL;
int ret;
if (timeout_msecs >= 0) {
to = &end_time;
poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,
NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
}
ret = do_sys_poll(ufds, nfds, to);
……
SYSCALL_DEFINE3
是一个宏,它定义于 include/linux/syscalls.h,展开后就有 sys_poll 函数。
sys_poll
对超时参数稍作处理后,直接调用 do_sys_poll。
do_sys_poll 函数
do_sys_poll 位于 fs/select.c 文件中,我们忽略其他代码,只看关键部分:
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
struct timespec64 *end_time)
{
……
poll_initwait(&table);
fdcount = do_poll(head, &table, end_time);
poll_freewait(&table);
……
}
poll_initwait 函数非常简单,它初始化一个 poll_wqueues 变量 table:
poll_initwait
init_poll_funcptr(&pwq->pt, __pollwait);
pt->qproc = qproc;
即 table->pt->qproc = __pollwait, __pollwait 将在驱动的 poll 函数里用到。do_poll 函数才是核心,继续看代码。
do_poll 函数
do_poll 函数位于 fs/select.c 文件中,这是 POLL 机制中最核心的代码,贴图如下:
① 从这里开始,将会导致驱动程序的 poll 函数被第一次调用
沿着②③④⑤,你可以看到:驱动程序里的poll_wait会调用__pollwait 函数把线程放入某个队列。
当执行完①之后,在⑥或⑦处, pt->_qproc 被设置为 NULL,所以第二次调用驱动程序的 poll 时,不会再次把线程放入某个队列里。
⑧ 如果驱动程序的 poll 返回有效值,则 count 非 0,跳出循环;
⑨ 否则休眠一段时间;当休眠时间到,或是被中断唤醒时,会再次循环、再次调用驱动程序的 poll。
回顾 APP 的代码, APP 可以指定“想等待某些事件”, poll 函数返回后,可以知道“发生了哪些事件”:
驱动程序里怎么体现呢?在上上一个图中,看②位置处,细说如下: