明天《升级版全系列嵌入式视频_入门篇》新增一节视频:19.2_POLL机制
时长24分钟,免费观看
何为POLL机制?
给驱动程序加一个闹铃,让APP不必死等数据;
既可以快速把握POLL机制,更有对内核代码的详尽讲解
多种观看形式:
一,B站(关注UP主-韦东山,视频更新后会有通知)
19.2_POLL机制:
(复制到浏览器打开)
二,官网()(支持在线观看,爽歪歪)
19.2_POLL机制:
(复制到浏览器打开,或则从步入)
温情提示:
为便捷及时通知您,您须要支付1毛钱订阅该专栏,
之后依次填写年纪,职位等信息。辛苦了。没有您的容许我们不会外泄这种信息。
三,
百度云盘(适宜不便捷上网的朋友)
密码:6FJk
路径:
->04_快速入门(即将开始)
->02_嵌入式linux驱动开发基础知识
->19.驱动程序基石
下边是视频文稿:
19.2POLL机制
19.2.1适用场景
在上面引入中断时,我们当初举过一个反例:
母亲如何晓得厨房里孩子醒了?
①时不时进卧室看一下:查询方法
简单,而且累
②进去屋子陪孩子一起午睡,男孩醒了会叫醒她:休眠-唤起
不累,然而母亲干不了活了
③妈妈要干好多活,而且可以陪孩子睡一会,定个闹铃:poll形式
要浪费点时间linux select调用机制,并且可以继续干活。
母亲要么是被儿子叫醒,要么是被闹铃叫醒。
④妈妈在卧室干活,女儿醒了他会自己走出大门告诉母亲:异步通知
母亲、小孩互不耽搁
使用休眠-唤起的形式等待某个风波发生时,有一个缺点:等待的时间可能许久。我们可以加上一个超时时间,这时就可以使用poll机制。
①APP不晓得驱动程序中是否有数据,可以先调用poll函数查询一下,poll函数可以传入超时时间;
②APP步入内核态,调用到驱动程序的poll函数,假若有数据的话立即返回;
③如果发觉没有数据时就休眠一段时间;
④当有数据时,例如当按下按钮时,驱动程序的中断服务程序被调用linux系统镜像下载,它会记录数据、唤醒APP;
⑤当超时时间到了以后,内核也会唤起APP;
⑥APP按照poll函数的返回值就可以晓得是否有数据,假如有数据就调用read得到数据
19.2.2使用流程
母亲步入卧室时,会先看孩子醒没醒,闹铃响以后走出屋子之前又会再看孩子醒没醒。
注意:看了2次女儿!
POLL机制也是类似的,流程如下:
函数执行流程如上图①~⑧所示,重点从③开始看。假定一开始无按钮数据:
③APP调用poll以后,步入内核态;
④导致驱动程序的drv_poll被调用:
注意,drv_poll要把自己这个线程挂入等待队列wq中;假定不装入队列里,那之后发生中断时,中断服务程序去哪儿找到你嘛?
drv_poll就会判定一下:有没有数据啊?返回这个状态。
⑤假设当前没有数据,则休眠一会;
⑥在休眠过程中,按下了键盘,发生了中断:
在中断服务程序里记录了键盘值,但是从wq中把线程唤起了。
⑦线程从休眠中被唤起,继续执行for循环,再度调用drv_poll:
drv_poll返回数据状态
⑧哦,你有数据,那从内核态返回到应用态吧
⑨APP调用read函数读数据
若果仍然没有数据,调用流程也是类似的,重点从③开始看,如下:
③APP调用poll以后,步入内核态;
④导致驱动程序的drv_poll被调用:
注意,drv_poll要把自己这个线程挂入等待队列wq中;假定不装入队列里,那之后发生中断时,中断服务程序去哪儿找到你嘛?
drv_poll就会判定一下:有没有数据啊?返回这个状态。
⑤假设当前没有数据,则休眠一会;
⑥在休眠过程中,始终没有按下了键盘,超时时间到:内核把这个线程唤起;
⑦线程从休眠中被唤起,继续执行for循环,再度调用drv_poll:
drv_poll返回数据状态
⑧哦,你还是没有数据linux手机,并且超时时间到了,那从内核态返回到应用态吧
⑨APP不能调用read函数读数据
注意几点:
①drv_poll要把线程挂入队列wq,而且并不是在drv_poll中步入休眠,而是在调用drv_poll以后休眠
②drv_poll要返回数据状态
③APP调用一次poll,有可能会造成drv_poll被调用2次
④线程被唤起的缘由有2:中断发生了去队列wq中把它唤起,超时时间到了内核把它唤起
⑤APP要判定poll返回的缘由:有数据,还是超时。有数据时再去调用read函数。
19.2.3驱动编程
使用poll机制时,驱动程序的核心就是提供对应的drv_poll函数。
在drv_poll函数中要做2件事:
①把当前线程挂入队列wq:poll_wait
APP调用一次poll,可能造成drv_poll被调用2次,并且我们并不须要把当前线程挂入队列2次。
可以使用内核的函数poll_wait把线程挂入队列,假如线程早已在队列里了,它就不会再度挂入。
②返回设备状态:
APP调用poll函数时,有可能是查询“有没有数据可以读”:POLLIN,也有可能是查询“你有没有空间给我写数据”:POLLOUT。
所以drv_poll要返回自己的当前状态:(POLLIN|POLLRDNORM)或(POLLOUT|POLLWRNORM)。
POLLRDNORM等同于POLLIN,为了兼容个别APP把它们一起返回。
POLLWRNORM等同于POLLOUT,为了兼容个别APP把它们一起返回。
APP调用poll后,很有可能会休眠。对应的,在键盘驱动的中断服务程序中,也要有唤起操作。
驱动程序中poll的代码如下:
static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{
printk("%s %s line %dn", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp, &gpio_key_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}
19.2.4应用编程
注意:APP可以调用poll或select函数,这2个函数的作用是一样的。
poll/select函数可以检测多个文件,可以检测多种风波:
风波类型
说明
POLLIN
有数据可读
POLLRDNORM
等同于POLLIN
POLLRDBAND
Prioritybanddatacanbereadlinux select调用机制,有优先级较较高的“banddata”可读
Linux系统中极少使用这个风波
POLLPRI
高优先级数据可读
POLLOUT
可以写数据
POLLWRNORM
等同于POLLOUT
POLLWRBAND
Prioritydatamaybewritten
POLLERR
发生了错误
POLLHUP
挂起
POLLNVAL
无效的恳求,通常是fd未open
在调用poll函数时,要指明:
①你要检测哪一个文件:哪一个fd
②你想检测这个文件的哪种风波:是POLLIN、还是POLLOUT
最后,在poll函数返回时,要判定状态。
应用程序代码如下:
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
fds[0].fd = fd;
fds[0].events = POLLIN;
ret = poll(fds, 1, timeout_ms);
if ((ret == 1) && (fds[0].revents & POLLIN))
{
read(fd, &val, 4);
printf("get button : 0x%xn", val);
}
19.2.5现场编程
看视频
19.2.6上机实验
看视频
19.2.7POLL机制的内核代码解读
LinuxAPP系统调用,基本都可以在它的名子前加上“sys_”前缀,这就是它在内核中对应的函数。例如系统调用open、read、write、poll,与之对应的内核函数为:sys_open、sys_read、sys_write、sys_poll。
对于系统调用poll或select,它们对应的内核函数都是sys_poll。剖析sys_poll,即可理解poll机制。
19.2.7.1sys_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。
19.2.7.2do_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函数才是核心,继续看代码。
19.2.7.3do_poll函数
do_poll函数坐落fs/select.c文件中,这是POLL机制中最核心的代码,贴图如下:
①从这儿开始,将会造成驱动程序的poll函数被第一次调用。
顺着②③④⑤,你可以听到:驱动程序里的poll_wait会调用__pollwait函数把线程装入某个队列。
当执行完①之后,在⑥或⑦处,pt->_qproc被设置为NULL,所以第二次调用驱动程序的poll时,不会再度把线程装入某个队列里。
⑧如果驱动程序的poll返回有效值,则count非0,跳出循环;
⑨否则休眠一段时间;当休眠时间到,或是被中断唤起时,会再度循环、再次调用驱动程序的poll。
回顾APP的代码,APP可以指定“想等待个别风波”,poll函数返回后,可以晓得“发生了什么风波”:
驱动程序里如何彰显呢?在上上一个图中,看②位置处,细说如下:
-☆END☆-
我是韦东山,10多年仍然在研究linux+ARM,希望我的分享对你有帮助,欢迎进店订阅我的付费内容:
往期好文:
欢迎加群与韦老师交流讨论: