首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >POLL机制

POLL机制

作者头像
开源519
发布2020-07-23 17:11:39
1.1K0
发布2020-07-23 17:11:39
举报
文章被收录于专栏:开源519开源519

01

1. poll机制适用场景

应用层采用超时机制访问驱动设备。即如果第一次访问可以使用直接返回,若不能访问,则先将应用层休眠,在到了设定的时间,再访问一次,此时可以访问则返回成功标志,若不能访问则返回失败。

02

2.poll机制实现流程

Poll机制会判断fds中的文件是否可读,如果可读则会立即返回,返回的值就是可读文件描述符fd的数量,如果不可读,那么进程就会休眠timeout这么长的时间,然后再来判断是否有文件可读,如果有,返回fd的数量,如果没有,则返回0。Poll机制的实现需要等待队列的支撑。

03

3.代码应用

在驱动里若要使用poll机制,只需要初始化一个等待队列,实例化file_operations中.poll成员即可。

驱动程序:

static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
……
static unsigned int drv_poll(struct file *fp, poll_table * wait)
{
    Unsigned int ret = 0;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &queue_wait, wait);
     if(atomic_read(&dev->releasekey)) { /* 按键按下 */
     ret = POLLIN | POLLRDNORM;        /* 返回 PLLIN */
 }
     return ret;
}
……
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
    .owner   = THIS_MODULE,
    .read    = gpio_key_drv_read,
    .poll    = gpio_key_drv_poll,
};
……

应用层实例:

……
while (1)
    {
        /* 3. 读文件 */
        ret = poll(fds, 1, timeout_ms);
        if ((ret == 1) && (fds[0].revents & POLLIN))
        {
            read(fd, &val, 4);
            printf("get button : 0x%x\n", val);
        }
        else
        {
            printf("timeout\n");
        }
    }
……

04

4.原理分析

从应用层一直到驱动,来分析poll机制实现与运行原理。

app: poll
      |
drv:sys_poll
      |
      — 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
        |
        - do_poll(head, &table, end_time);
        |
        _ for ( walk = head; walk; walk = walk->next )
          {
                for (; pfd != pfd_end; pfd++) {    /* 可以监测多个驱动设备所产生的事件 */
                    if (do_pollfd(pfd, pt)) {  
                      |
                      _  mask = file->f_op->poll(file, pwait); > 实际效果:进入我们写的drivers_poll(file,pwait)
                                |
                                 _  poll_wait(file, &button_waitq, wait); > 实际效果:进入__pollwait(file, &button_waitq, wait),也就是将进程挂接到button_waitq等待队列下
                                          |
                                          _ if (p && p->_qproc && wait_address)
                                                p->_qproc(filp, wait_address, p); > 将线程放入等待队列
                                            |
                                             _ add_wait_queue()
                                |
                                —  mask赋值 ; return mask; /* 返回事件类型 */
                         pollfd->revents = mask;    /* 将实际事件类型返回 */
                         count++;
                         pt->_qproc = NULL;         //这个地方清空,下次调用驱动poll,就不会将进程挂入等待队列
                     } 
                 } 
               if (count || timed_out) /* 如果有事件发生,或者超时,则跳出poll */ 
                  break; 
               if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)) /* 如果没有事件发生,那么陷入休眠状态 */ 
                  timed_out = 1; 
          }

基本上,知道大概流程就可以了。其流程的实现大多依靠函数指针为基础,通过向指针函数传参,以及相关的调用配合编程思想实现相应的功能。在浏览时,不必纠结过多的细节,这也是最初笔者容易忽视的。往往纠结于某个细节而耽搁过多时间,将时间浪费在不必要的地方,虽然时间在当下看是一直富裕的,实则也是一种代价,只有在会用以后,才能够更快的去了解其中的原理。当然,另一个角度来讲,linux的实现很伟大,每一个细节都值得学习,所以当时间足够富裕时,建议多看看每一个流程的细节的衔接。

参考资料: https://www.cnblogs.com/amanlikethis/p/6915485.html     《嵌入式linux应用开发完全手册V2.3_韦东山》

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源519 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档