zoukankan      html  css  js  c++  java
  • 8.中断按键驱动程序之poll机制(详解)

    本节继续在上一节中断按键程序里改进,添加poll机制.

    那么我们为什么还需要poll机制呢。之前的测试程序是这样:

    while (1)
    {
    read(fd, &key_val, 1);
    printf("key_val = 0x%x
    ", key_val);
    }

    在没有poll机制的情况下,大部分时间程序都处在read中休眠的那个位置。如果我们不想让程序停在这个位置,而是希望当有按键按下时,我们再去read,因此我们编写poll函数,测试程序调用poll函数根据返回值,来决定是否执行read函数。

    poll机制作用:相当于定时器,设置一定时间使进程等待资源,如果时间到了中断还处于睡眠状态(等待队列),poll机制就会唤醒中断,获取一次资源

     

    1.poll机制内核框架

    如下图所示,在用户层上,使用poll或select函数时,和open、read那些函数一样,也要进入内核sys_poll函数里,接下来我们分析sys_poll函数来了解poll机制(位于/fs/select.c)

     

    1.1 sys_poll代码如下:

    asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
    {
            if (timeout_msecs > 0)    //参数timeout>0
        {
             timeout_jiffies = msecs_to_jiffies(timeout_msecs);  //通过频率来计算timeout时间需要多少计数值
        }
        else
        {
              timeout_jiffies = timeout_msecs;    //如果timeout时间为0,直接赋值
           }
      return do_sys_poll(ufds, nfds, &timeout_jiffies);   //调用do_sys_poll。
    }

    1.2 然后进入do_sys_poll(位于fs/select.c):

    int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
    {
      ... ...
      /*初始化一个poll_wqueues变量table*/
      poll_initwait(&table);
      ... ...
      fdcount = do_poll(nfds, head, &table, timeout);
      ... ... 
    }

    1.3进入poll_initwait函数,发现主要实现以下一句,后面会分析这里:

    table ->pt-> qproc=__pollwait;    //__pollwait将在驱动的poll函数里的poll_wait函数用到

    1.4然后进入do_poll函数, (位于fs/select.c):

    static int do_poll(unsigned int nfds,  struct poll_list *list, struct poll_wqueues *wait,  s64 *timeout)
    {
      ……
           for (;;)
       {
        ……
        set_current_state(TASK_INTERRUPTIBLE);       //设置为等待队列状态
        ......
           for (; pfd != pfd_end; pfd++) {             //for循环运行多个poll机制
                       /*将pfd和pt参数代入我们驱动程序里注册的poll函数*/
                            if (do_pollfd(pfd, pt))     //若返回非0,count++,后面并退出
                  {  count++;
                               pt = NULL; } }
    
        ……
    
        /*count非0(.poll函数返回非0),timeout超时计数到0,有信号在等待*/
    
           if (count || !*timeout || signal_pending(current))
                                break;
        ……
      
        /*进入休眠状态,只有当timeout超时计数到0,或者被中断唤醒才退出,*/
             __timeout = schedule_timeout(__timeout);
    
        ……
    
       }
    
    __set_current_state(TASK_RUNNING);  //开始运行
    return count;
    
    }

    1.4.1上面do_pollfd函数到底是怎么将pfd和pt参数代入的?代码如下(位于fs/select.c):

    static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
    {
          ……
             if (file->f_op && file->f_op->poll)
             mask = file->f_op->poll(file, pwait);
          ……
    
    return mask;
    }

    上面file->f_op 就是我们驱动里的file_oprations结构体,如下图所示:

     

    所以do_pollfd(pfd, pt)就执行了我们驱动程序里的.poll(pfd, pt)函数(第2小节开始分析.poll函数)

    1.4.2当poll进入休眠状态后,又是谁来唤醒它?这就要分析我们的驱动程序.poll函数(第2小节开始分析.poll函数)

    2写驱动程序.poll函数,并分析.poll函数:

    在上一节驱动程序里添加以下代码:

      #include <linux/poll.h>                //添加头文件
      
    /* .poll驱动函数: third_poll */ static unsigned int third_poll(struct file *fp, poll_table * wait) //fp:文件 wait: { unsigned int mask =0; poll_wait(fp, &button_wait, wait); if(even_press) //中断事件标志, 1:退出休眠状态 0:进入休眠状态 mask |= POLLIN | POLLRDNORM ; return mask; //当超时,就返给应用层为0 ,被唤醒了就返回POLLIN | POLLRDNORM ; } static struct file_operations third_drv_fops={ .owner = THIS_MODULE, .open = third_drv_open, .read = third_drv_read,    .release=third_drv_class,    .poll = third_poll, //创建.poll函数 };

    2.1 在我们1.4小节do_poll函数有一段以下代码:

    if (do_pollfd(pfd, pt))     //若返回非0,count++,后面并退出
    {      
         count
    ++; pt = NULL; }

    且在1.4.1分析出: do_pollfd(pfd, pt)就是指向的驱动程序third_poll()函数,

    所以当我们有按键按下时, 驱动函数third_poll()就会返回mask非0值,然后在内核函数do_poll里的count就++,poll机制并退出睡眠.

    2.2分析在内核中poll机制如何被驱动里的中断唤醒的 

    在驱动函数third_poll()里有以下一句:

     poll_wait(fp, &button_wait, wait);

     

    如上图所示,代入参数,poll_wait()就是执行了: p->qproc(filp, button_wait, p);

    刚好对应了我们1.3小节的:      

    table ->pt-> qproc=__pollwait;

    所以poll_wait()函数就是调用了: __pollwait(filp, button_wait, p);

    然后我们来分析__pollwait函数,pollwait的代码如下:

    static void __pollwait(struct file  *filp, wait_queue_head_t  *wait_address,poll_table  *p)
    {
       ... ...
       //把current进程挂载到&entry->wait下
       init_waitqueue_entry(&entry->wait, current);
    
       //再&entry->wait把添加到到button_wait中断下
       add_wait_queue(wait_address, &entry->wait);
    
    }

    它是将poll进程添加到了button_wait中断队列里,这样,一有按键按下时,在中断服务函数里就会唤醒button_wait中断,同样也会唤醒poll机制,使poll机制重新进程休眠计数

    2.3 驱动程序.poll函数返回值介绍

    当中断休眠状态时,返回mask为0

    当运行时返回:mask |= POLLIN | POLLRDNORM

    其中参数意义如下:

    常量

    说明

    POLLIN

    普通或优先级带数据可读

    POLLRDNORM

    normal普通数据可读

    POLLRDBAND

    优先级带数据可读

    POLLPRI

    Priority高优先级数据可读

    POLLOUT

    普通数据可写

    POLLWRNORM

    normal普通数据可写

    POLLWRBAND

    band优先级带数据可写

    POLLERR

    发生错误

    POLLHUP

    发生挂起

    POLLNVAL

    描述字不是一个打开的文件

    所以POLLIN | POLLRDNORM:普通数据可读|优先级带数据可读

    mask就返回到应用层poll函数,

     

     

    3.改进测试程序third_poll_text.c(添加poll函数)

    在linux中可以通过man poll 来查看poll函数如何使用

    poll函数原型如下(#include <poll.h>):

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    参数介绍:

    1) *fds:是一个poll描述符结构体数组(可以处理多个poll),结构体pollfd如下:

      struct pollfd {
                   int   fd;         /* file descriptor 文件描述符*/
                   short events;     /* requested events 请求的事件*/
                   short revents;    /* returned events 返回的事件(函数返回值)*/
               };

    其中events和revents值参数如下:

    常量

    说明

    POLLIN

    普通或优先级带数据可读

    POLLRDNORM

    normal普通数据可读

    POLLRDBAND

    优先级带数据可读

    POLLPRI

    Priority高优先级数据可读

    POLLOUT

    普通数据可写

    POLLWRNORM

    normal普通数据可写

    POLLWRBAND

    band优先级带数据可写

    POLLERR

    发生错误

    POLLHUP

    发生挂起

    POLLNVAL

    描述字不是一个打开的文件

     

    2) nfds:表示多少个poll,如果1个,就填入1

    3) timeout:定时多少ms

    返回值介绍:

    返回值为0:表示超时或者fd文件描述符无法打开

    返回值为 -1:表示错误

    返回值为>0时 :就是以下几个常量

    常量

    说明

    POLLIN

    普通或优先级带数据可读

    POLLRDNORM

    normal普通数据可读

    POLLRDBAND

    优先级带数据可读

    POLLPRI

    Priority高优先级数据可读

    POLLOUT

    普通数据可写

    POLLWRNORM

    normal普通数据可写

    POLLWRBAND

    band优先级带数据可写

    POLLERR

    发生错误

    POLLHUP

    发生挂起

    POLLNVAL

    描述字不是一个打开的文件

    最终改进的测试代码如下:

    #include <sys/types.h>    
    #include <sys/stat.h>    
    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <poll.h>                 //添加poll头文件
    
    
    /*useg:    thirdtext   */
    int main(int argc,char **argv)
    {
      int fd,ret;
      unsigned int val=0;
      struct pollfd fds;                          //定义poll文件描述结构体               
      fd=open("/dev/buttons",O_RDWR);          
    if(fd<0) {printf("can't open!!! "); return -1;} fds.fd=fd; fds.events= POLLIN; //请求类型是 普通或优先级带数据可读 while(1) { ret=poll(&fds,1,5000) ; //一个poll, 定时5000ms,进入休眠状态 if(ret==0) //超时 { printf("time out "); } else if(ret>0) //poll机制被唤醒,表示有数据可读 { read(fd,&val,1); //读取一个值 printf("key_val=0X%x ",val); } } return 0; }

     效果如下:

    若5S没有数据,则打印time out

    下节开始学习——使用异步通知来通知信号

  • 相关阅读:
    关于这个 blog
    P6499 [COCI2016-2017#2] Burza 题解
    CF1172F Nauuo and Bug 题解
    CF1479D Odd Mineral Resource 题解
    CF1442E Black, White and Grey Tree 题解
    CF1442D Sum 题解
    CF1025D Recovering BST 题解
    CF1056E Check Transcription 题解
    CF1025F Disjoint Triangles 题解
    红包算法的PHP实现
  • 原文地址:https://www.cnblogs.com/lifexy/p/7508633.html
Copyright © 2011-2022 走看看