完善 poll 实现机制.

Signed-off-by: lion.chan <cy187lion@sina.com>
This commit is contained in:
lion.chan 2022-05-08 11:29:44 +08:00
parent 8d32078452
commit 88ffd087de
1 changed files with 11 additions and 9 deletions

View File

@ -337,7 +337,7 @@ out_cdev:
}
```
上述代码展示了一个 poll() 函数功能以及具体驱动的实现细节。利用这样的框架,我们可以写出类似驱动的 poll() 功能。但是,这个框架很难理解,不知道为什么这样编写。为此,需要了解 Linux 系统 poll 功能实现的机制。
上述代码展示了一个 poll() 函数功能以及具体驱动的实现细节。利用这样的框架,我们可以写出类似驱动的 poll() 功能。其中比较关键的函数 poll_wait() 比较具有迷惑性,虽然名字中带有 wait但这个函数本身并不会真正的阻塞因此整个 poll 调用中,不包含任何等待条件满足的程序,这个框架代码开始变得很难理解。想要清楚为何这样编写,就需要了解 Linux 系统 poll 功能实现的机制。
## Linux 内核 poll 实现机制
@ -348,22 +348,22 @@ app: poll
|
drv:sys_poll
|
do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, struct timespec * end_time)
+- do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, struct timespec * end_time)
|
- poll_initwait(&table); // 实际效果:令函数指针 table.pt.qproc = __pollwait这个函数指针最终会传递给 poll_wait() 函数调用中的 wait->qproc
+- poll_initwait(&table); // 实际效果:令函数指针 table.pt.qproc = __pollwait这个函数指针最终会传递给 poll_wait() 函数调用中的 wait->qproc
|
- do_poll(nfds, head, &table, end_time);
+- do_poll(nfds, head, &table, end_time);
|
_ for ( ; ; )
+- for ( ; ; )
{
for (; pfd!=pfd_end; pfd++) { // 可以监测多个驱动设备所产生的事件
if (do_pollfd(pfd, pt)) {
|
_ mask = f.file->f.f_op->poll(f.file, pwait); // 实际效果:执行驱动中的 demo_poll(file, pwait)
+- mask = f.file->f.f_op->poll(f.file, pwait); // 实际效果:执行驱动中的 demo_poll(file, pwait)
|
_ poll_wait(file, &button_waitq, wait); // 实际效果:执行 __pollwait(file, &button_waitq, wait),也就是将进程挂接到 button_waitq 等待队列下
+- poll_wait(filp, &devp->r_wait, wait); // 实际效果:执行 poll_wait(filp, &devp->r_wait, wait),也就是将进程挂接到 devp->r_wait 等待队列下
|
mask赋值; return mask; // 返回事件类型
+- mask赋值; return mask; // 返回事件类型
pollfd->revents = mask; // 将实际事件类型返回
count++; pt = NULL;
@ -372,8 +372,10 @@ drv:sys_poll
if (count || timed_out) // 如果有事件发生,或者超时,则跳出 poll
break;
if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)) // 如果没有事件发生,那么陷入休眠状态
|
+- schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
timed_out = 1;
}
```
由此可见demo_poll() 函数,是系统在执行 sys_poll() 过程中的一个调用,调用的目的是“将进程挂接到等待队列下”和“返回事件类型 mask”。当已经发生了请求事件那么通过标记 mask 非 0则 if(do_pollfd(pfd, pt)) 判断为真,令 count++,从而可以直接令 poll() 函数成功返回。如果还没有发生请求的事件,那么 mask 被标记为 0进程将通过函数 poll_schedule_timeout() 陷入休眠状态。因为之前已经将进程挂接到等待队列下,所以一旦发生了请求的事件,则进程将被唤醒,重新执行 demo_poll(),而显然此时能够成功返回。
由此可见demo_poll() 函数,是系统在执行 sys_poll() 过程中的一个调用,调用的目的是“将进程挂接到等待队列下”和“返回事件类型 mask”。当已经发生了请求事件那么通过标记 mask 非 0则 if(do_pollfd(pfd, pt)) 判断为真,令 count++,从而可以直接令 poll() 函数成功返回。如果还没有发生请求的事件,那么 mask 被标记为 0进程将通过函数 poll_schedule_timeout() 陷入休眠状态,直到定时器到时或者等待队列被触发。因为之前已经在驱动中通过 poll_wait() 将进程挂接到等待队列下,所以一旦发生了请求的事件,则进程将被唤醒,重新执行 demo_poll(),而显然此时能够成功返回。