目录
1 适用场景
2 POLL机制的动开内核代码详解
2.1 sys_poll 函数
2.2 do_sys_poll 函数
2.3 do_poll函数
3 poll机制使用流程
4 驱动编程
5 应用编程
6 代码
6.1 gpio_key_drv.c
6.3 Makefile
可以看 字符设备驱动程序之poll机制 那篇文章中的机制分析以及代码去理解POLL机制,看完那篇文章也就理解POLL机制了,下面的这篇文章也可以不看。
在前面引入中断时,我们曾经举过一个例子:妈妈怎么知道卧室里小孩醒了?
poll 方式:妈妈要干很多活,但是发基可以陪小孩睡一会,定个闹钟,要浪费点时间,但是可以继续干活
。妈妈要么是动开被小孩吵醒,要么是被闹钟吵醒。
使用休眠-唤醒的发基方式等待某个事件发生时,有一个缺点:等待的时间可能很久
。我们可以加上一个超时时间,这时就可以使用 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 位于 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 位于 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 函数位于 fs/select.c 文件中,这是 POLL 机制中最核心的代码,贴图如下:
① 从这里开始,将会导致驱动程序的 poll 函数被第一次调用
。 沿着②③④⑤,你可以看到:驱动程序里的 poll_wait 会调用__pollwait函数把线程放入某个队列
。
当执行完①之后,在⑥或⑦处,pt->_qproc 被设置为 NULL,所以第二次调用驱动程序的 poll 时,不会再次把线程放入某个队列里