From 88ffd087de8f94db57177f8c0cecd861dccaf648 Mon Sep 17 00:00:00 2001 From: "lion.chan" Date: Sun, 8 May 2022 11:29:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20poll=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=9C=BA=E5=88=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: lion.chan --- .../8.8_设备驱动中的阻塞与非阻塞_IO.md | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Chapter8_SOC_与_Linux/8.8_设备驱动中的阻塞与非阻塞_IO.md b/Chapter8_SOC_与_Linux/8.8_设备驱动中的阻塞与非阻塞_IO.md index 4ce5227..61d2b2e 100644 --- a/Chapter8_SOC_与_Linux/8.8_设备驱动中的阻塞与非阻塞_IO.md +++ b/Chapter8_SOC_与_Linux/8.8_设备驱动中的阻塞与非阻塞_IO.md @@ -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(),而显然此时能够成功返回。