• 3.字符设备驱动------Poll机制


    1.poll情景描述

      以之前的按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值。

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

      这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。

          这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行动来解决问题。

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

    2.poll机制内核框架

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

    2.1 sys_poll:

       sys_poll函数会对超时参数timeout_msecs作简单处理后调用do_sys_poll

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

    2.2 do_sys_poll

     函数do_sys_poll会调用poll_initwait来初始化一个struct poll_wqueues table,也就是table->pt->qproc = __pollwait__pollwait将在驱动的poll函数里用到,接着调用do_poll

     1 int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
     2 {
     3     struct poll_wqueues table;
     4      int fdcount, err;
     5      unsigned int i;
     6     struct poll_list *head;
     7      struct poll_list *walk;
     8     /* Allocate small arguments on the stack to save memory and be
     9        faster - use long to make sure the buffer is aligned properly
    10        on 64 bit archs to avoid unaligned access */
    11     long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
    12     struct poll_list *stack_pp = NULL;
    13 
    14     /* Do a sanity check on nfds ... */
    15     if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
    16         return -EINVAL;
    17 
    18     poll_initwait(&table);
    19 
    20     head = NULL;
    21     walk = NULL;
    22     i = nfds;
    23     err = -ENOMEM;
    24     while(i!=0) {
    25         struct poll_list *pp;
    26         int num, size;
    27         if (stack_pp == NULL)
    28             num = N_STACK_PPS;
    29         else
    30             num = POLLFD_PER_PAGE;
    31         if (num > i)
    32             num = i;
    33         size = sizeof(struct poll_list) + sizeof(struct pollfd)*num;
    34         if (!stack_pp)
    35             stack_pp = pp = (struct poll_list *)stack_pps;
    36         else {
    37             pp = kmalloc(size, GFP_KERNEL);
    38             if (!pp)
    39                 goto out_fds;
    40         }
    41         pp->next=NULL;
    42         pp->len = num;
    43         if (head == NULL)
    44             head = pp;
    45         else
    46             walk->next = pp;
    47 
    48         walk = pp;
    49         if (copy_from_user(pp->entries, ufds + nfds-i, 
    50                 sizeof(struct pollfd)*num)) {
    51             err = -EFAULT;
    52             goto out_fds;
    53         }
    54         i -= pp->len;
    55     }
    56 
    57     fdcount = do_poll(nfds, head, &table, timeout);
    58 
    59     /* OK, now copy the revents fields back to user space. */
    60     walk = head;
    61     err = -EFAULT;
    62     while(walk != NULL) {
    63         struct pollfd *fds = walk->entries;
    64         int j;
    65 
    66         for (j=0; j < walk->len; j++, ufds++) {
    67             if(__put_user(fds[j].revents, &ufds->revents))
    68                 goto out_fds;
    69         }
    70         walk = walk->next;
    71       }
    72     err = fdcount;
    73     if (!fdcount && signal_pending(current))
    74         err = -EINTR;
    75 out_fds:
    76     walk = head;
    77     while(walk!=NULL) {
    78         struct poll_list *pp = walk->next;
    79         if (walk != stack_pp)
    80             kfree(walk);
    81         walk = pp;
    82     }
    83     poll_freewait(&table);
    84     return err;
    85 }
    do_sys_poll
    1 int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
    2 {
    3   ... ...
    4   /*初始化一个poll_wqueues变量table*/
    5   poll_initwait(&table);
    6   ... ...
    7   fdcount = do_poll(nfds, head, &table, timeout);
    8   ... ... 
    9 }

      2.2.1 进入poll_initwait

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

     1 poll_initwait(&table);
     2 
     3 void poll_initwait(struct poll_wqueues *pwq)
     4 {
     5     init_poll_funcptr(&pwq->pt, __pollwait);
     6     pwq->error = 0;
     7     pwq->table = NULL;
     8     pwq->inline_index = 0;
     9 }
    10 
    11 static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
    12 {
    13     pt->qproc = qproc;
    14 }
    15 
    16 也就是最终是
    17 
    18 table->pt->qproc=__pollwait

      那么_pollwait做了什么

     1 /* Add a new entry */
     2 static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
     3                 poll_table *p)
     4 {
     5     struct poll_table_entry *entry = poll_get_entry(p);
     6     if (!entry)
     7         return;
     8     get_file(filp);
     9     entry->filp = filp;
    10     entry->wait_address = wait_address;
    11     init_waitqueue_entry(&entry->wait, current);
    12     add_wait_queue(wait_address, &entry->wait);  /*将当前进程current挂到队列里面去*/
    13 }

       2.2.2 进入 do_poll

     1 static int do_poll(unsigned int nfds,  struct poll_list *list,
     2            struct poll_wqueues *wait, s64 *timeout)
     3 {
     4     int count = 0;
     5     poll_table* pt = &wait->pt;
     6 
     7     /* Optimise the no-wait case */
     8     if (!(*timeout))
     9         pt = NULL;
    10  
    11     for (;;) {
    12         struct poll_list *walk;
    13         long __timeout;
    14 
    15         set_current_state(TASK_INTERRUPTIBLE);
    16         for (walk = list; walk != NULL; walk = walk->next) {
    17             struct pollfd * pfd, * pfd_end;
    18 
    19             pfd = walk->entries;
    20             pfd_end = pfd + walk->len;
    21             for (; pfd != pfd_end; pfd++) {
    22                 /*
    23                  * Fish for events. If we found one, record it
    24                  * and kill the poll_table, so we don't
    25                  * needlessly register any other waiters after
    26                  * this. They'll get immediately deregistered
    27                  * when we break out and return.
    28                  */
    29                 if (do_pollfd(pfd, pt)) {
    30                     count++;
    31                     pt = NULL;
    32                 }
    33             }
    34         }
    35         /*
    36          * All waiters have already been registered, so don't provide
    37          * a poll_table to them on the next loop iteration.
    38          */
    39         pt = NULL;
    40         if (count || !*timeout || signal_pending(current))
    41             break;
    42         count = wait->error;
    43         if (count)
    44             break;
    45 
    46         if (*timeout < 0) {
    47             /* Wait indefinitely */
    48             __timeout = MAX_SCHEDULE_TIMEOUT;
    49         } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {
    50             /*
    51              * Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in
    52              * a loop
    53              */
    54             __timeout = MAX_SCHEDULE_TIMEOUT - 1;
    55             *timeout -= __timeout;
    56         } else {
    57             __timeout = *timeout;
    58             *timeout = 0;
    59         }
    60 
    61         __timeout = schedule_timeout(__timeout);
    62         if (*timeout >= 0)
    63             *timeout += __timeout;
    64     }
    65     __set_current_state(TASK_RUNNING);
    66     return count;
    67 }
    do_poll
     1 static int do_poll(unsigned int nfds,  struct poll_list *list, struct poll_wqueues *wait,  s64 *timeout)
     2 {
     3   ……
     4        for (;;)
     5    {
     6     ……
     7     set_current_state(TASK_INTERRUPTIBLE);       //设置为等待队列状态
     8     ......
     9        for (; pfd != pfd_end; pfd++) {             //for循环运行多个poll机制
    10                    /*将pfd和pt参数代入我们驱动程序里注册的poll函数*/
    11                         if (do_pollfd(pfd, pt))     //若返回非0,count++,后面并退出
    12               {  count++;
    13                            pt = NULL; } }
    14 
    15     ……
    16 
    17     /*count非0(.poll函数返回非0),timeout超时计数到0,有信号在等待*/
    18 
    19        if (count || !*timeout || signal_pending(current))
    20                             break;
    21     ……
    22   
    23     /*进入休眠状态,只有当timeout超时计数到0,或者被中断唤醒才退出,*/
    24          __timeout = schedule_timeout(__timeout);
    25 
    26     ……
    27 
    28    }
    29 
    30 __set_current_state(TASK_RUNNING);  //开始运行
    31 return count;
    32 
    33 }

         2.2.2.1 do_poll中的do_pollfd函数到底是怎么将pfd和pt参数代入的?

      if (do_pollfd(pfd, pt))  -->>>mask = file->f_op->poll(file, pwait);   return mask;

    1 static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
    2 {
    3       ……
    4          if (file->f_op && file->f_op->poll)
    5          mask = file->f_op->poll(file, pwait);  //f_op 即 file_operation结构体
    6       ……
    7 
    8 return mask;
    9 }

       当poll进入休眠状态后,又是谁来唤醒它?这就要分析我们的驱动程序.poll函数

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

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

     1 static unsigned int forth_drv_poll(struct file *file, poll_table *wait)
     2 {
     3     unsigned int mask = 0;
     4     poll_wait(file, &button_waitq, wait);    //将进程挂到队列里,不会立即休眠
     5 
     6     if (ev_press)    //中断事件标志, 1:退出休眠状态     0:进入休眠状态 
     7         mask |= POLLIN | POLLRDNORM;    //普通数据可读|优先级带数据可读
     8     return mask;    //当超时,就返给应用层为0 ,被唤醒了就返回POLLIN | POLLRDNORM 
     9 }
    10 
    11 static struct file_operations forth_drv_fops = {
    12     .owner   = THIS_MODULE,
    13     .open    = forth_drv_open,
    14     .read    = forth_drv_read,
    15     .release = forth_drv_close,
    16     .poll    = forth_drv_poll,    //创建.poll函数
    17 };

    4.测试·应用程序

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <stdio.h>
     5 #include <poll.h>
     6 
     7 
     8 /* forthdrvtest 
     9   */
    10 int main(int argc, char **argv)
    11 {
    12     int fd;
    13     unsigned char key_val;
    14     int ret;
    15 
    16     struct pollfd fds[1];
    17     
    18     fd = open("/dev/buttons", O_RDWR);
    19     if (fd < 0)
    20     {
    21         printf("can't open!
    ");
    22     }
    23 
    24     fds[0].fd     = fd;
    25     fds[0].events = POLLIN;
    26     while (1)
    27     {
    28         ret = poll(fds, 1, 5000);
    29         if (ret == 0)
    30         {
    31             printf("time out
    ");
    32         }
    33         else
    34         {
    35             read(fd, &key_val, 1);
    36             printf("key_val = 0x%x
    ", key_val);
    37         }
    38     }
    39     
    40     return 0;
    41 }
    forthdrv_test.c

    驱动程序

      1 #include <linux/module.h>
      2 #include <linux/kernel.h>
      3 #include <linux/fs.h>
      4 #include <linux/init.h>
      5 #include <linux/delay.h>
      6 #include <linux/irq.h>
      7 #include <asm/uaccess.h>
      8 #include <asm/irq.h>
      9 #include <asm/io.h>
     10 #include <asm/arch/regs-gpio.h>
     11 #include <asm/hardware.h>
     12 #include <linux/poll.h>
     13 
     14 
     15 static struct class *forthdrv_class;
     16 static struct class_device    *forthdrv_class_dev;
     17 
     18 volatile unsigned long *gpfcon;
     19 volatile unsigned long *gpfdat;
     20 
     21 volatile unsigned long *gpgcon;
     22 volatile unsigned long *gpgdat;
     23 
     24 
     25 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
     26 
     27 /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
     28 static volatile int ev_press = 0;
     29 
     30 
     31 struct pin_desc{
     32     unsigned int pin;
     33     unsigned int key_val;
     34 };
     35 
     36 
     37 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
     38 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
     39 static unsigned char key_val;
     40 
     41 struct pin_desc pins_desc[4] = {
     42     {S3C2410_GPF0, 0x01},
     43     {S3C2410_GPF2, 0x02},
     44     {S3C2410_GPG3, 0x03},
     45     {S3C2410_GPG11, 0x04},
     46 };
     47 
     48 
     49 /*
     50   * 确定按键值
     51   */
     52 static irqreturn_t buttons_irq(int irq, void *dev_id)
     53 {
     54     struct pin_desc * pindesc = (struct pin_desc *)dev_id;
     55     unsigned int pinval;
     56     
     57     pinval = s3c2410_gpio_getpin(pindesc->pin);
     58 
     59     if (pinval)
     60     {
     61         /* 松开 */
     62         key_val = 0x80 | pindesc->key_val;
     63     }
     64     else
     65     {
     66         /* 按下 */
     67         key_val = pindesc->key_val;
     68     }
     69 
     70     ev_press = 1;                  /* 表示中断发生了 */
     71     wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
     72 
     73     
     74     return IRQ_RETVAL(IRQ_HANDLED);
     75 }
     76 
     77 static int forth_drv_open(struct inode *inode, struct file *file)
     78 {
     79     /* 配置GPF0,2为输入引脚 */
     80     /* 配置GPG3,11为输入引脚 */
     81     request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
     82     request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
     83     request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
     84     request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);    
     85 
     86     return 0;
     87 }
     88 
     89 ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
     90 {
     91     if (size != 1)
     92         return -EINVAL;
     93 
     94     /* 如果没有按键动作, 休眠 */
     95     wait_event_interruptible(button_waitq, ev_press);
     96 
     97     /* 如果有按键动作, 返回键值 */
     98     copy_to_user(buf, &key_val, 1);
     99     ev_press = 0;
    100     
    101     return 1;
    102 }
    103 
    104 
    105 int forth_drv_close(struct inode *inode, struct file *file)
    106 {
    107     free_irq(IRQ_EINT0, &pins_desc[0]);
    108     free_irq(IRQ_EINT2, &pins_desc[1]);
    109     free_irq(IRQ_EINT11, &pins_desc[2]);
    110     free_irq(IRQ_EINT19, &pins_desc[3]);
    111     return 0;
    112 }
    113 
    114 static unsigned forth_drv_poll(struct file *file, poll_table *wait)
    115 {
    116     unsigned int mask = 0;
    117     poll_wait(file, &button_waitq, wait); // 不会立即休眠
    118 
    119     if (ev_press)
    120         mask |= POLLIN | POLLRDNORM;
    121 
    122     return mask;
    123 }
    124 
    125 
    126 
    127 static struct file_operations sencod_drv_fops = {
    128     .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    129     .open    =  forth_drv_open,     
    130     .read     =    forth_drv_read,       
    131     .release =  forth_drv_close,
    132     .poll    =  forth_drv_poll,
    133 };
    134 
    135 
    136 int major;
    137 static int forth_drv_init(void)
    138 {
    139     major = register_chrdev(0, "forth_drv", &sencod_drv_fops);
    140 
    141     forthdrv_class = class_create(THIS_MODULE, "forth_drv");
    142 
    143     forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
    144 
    145     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    146     gpfdat = gpfcon + 1;
    147 
    148     gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    149     gpgdat = gpgcon + 1;
    150 
    151     return 0;
    152 }
    153 
    154 static void forth_drv_exit(void)
    155 {
    156     unregister_chrdev(major, "forth_drv");
    157     class_device_unregister(forthdrv_class_dev);
    158     class_destroy(forthdrv_class);
    159     iounmap(gpfcon);
    160     iounmap(gpgcon);
    161     return 0;
    162 }
    163 
    164 
    165 module_init(forth_drv_init);
    166 
    167 module_exit(forth_drv_exit);
    168 
    169 MODULE_LICENSE("GPL");
    forth_drv.c

    5.相关参数:

    POLLIN相关:

     1 常量                说明
     2     
     3 POLLIN            普通或优先级带数据可读
     4 
     5 POLLRDNORM    normal普通数据可读
     6 
     7 POLLRDBAND    优先级带数据可读
     8 
     9 POLLPRI            Priority高优先级数据可读
    10 
    11 POLLOUT            普通数据可写
    12 
    13 POLLWRNORM    normal普通数据可写
    14 
    15 POLLWRBAND     band优先级带数据可写
    16 
    17 POLLERR            发生错误
    18 
    19 POLLHUP            发生挂起
    20 
    21 POLLNVAL            描述字不是一个打开的文件                        
    POLLxxx

    poll函数相关

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

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

    1   struct pollfd {
    2                int   fd;         /* file descriptor 文件描述符*/
    3                short events;     /* requested events 请求的事件 --其中events=POLLIN表示期待有数据读取.*/
    4                short revents;    /* returned events 返回的事件(函数返回值)*/
    5            };

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

      3) timeout:定时多少ms

    6.调用流程

    (1)当应用程序调用poll函数的时候,会调用到系统调用sys_poll函数,该函数最终调用do_poll函数

    (2)do_poll函数中有一个死循环,在里面又会利用do_pollfd函数去调用驱动中的poll函数(fds中每个成员的字符驱动程序都会被扫描到),

    (3)驱动程序中的Poll函数的工作 有两个

      ①调用poll_wait 函数,把进程挂到等待队列中去(这个是必须的,你要睡眠,必须要在一个等待队列上面,否则到哪里去唤醒你呢??),

      ②另一个是确定相关的fd是否有内容可读,如果可读,就返回1,否则返回0,如果返回1 ,do_poll函数中的count++

    (4)  do_poll函数中判断三个条件(if (count ||!timeout || signal_pending(current)))

      ①如果成立就直接跳出,

      ②如果不成立,就睡眠timeoutjiffes这么长的时间(调用schedule_timeout实现睡眠),

      如果在这段时间内没有其他进程去唤醒它,那么第二次执行判断的时候就会跳出死循环。(????????不懂)

      如果在这段时间内有其他进程唤醒它,那么也可以跳出死循环返回

       (例如我们可以利用中断处理函数去唤醒它,这样的话一有数据可读,就可以让它立即返回)。

    7.测试

    加载驱动后可以发现超时后会打印超时,按键有输出

    # insmod dri.ko
    # ./test /dev/xyz0
    time out
    irq55
    key_val = 0x3
    irq55
    key_val = 0x83

    ps查询是休眠状态,top查询cpu也比较低

    #ps
      PID  Uid        VSZ Stat Command
    781 0          1312 S   ./test /dev/xyz0
    
    #top
      PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND
      781   770 0        S     1312   2%   0% ./test /dev/xyz0

    参考文章:

    linux字符驱动之poll机制按键驱动

    8.中断按键驱动程序之poll机制(详解)

     

    字符设备驱动(六)按键poll机制

  • 相关阅读:
    Alpha 冲刺 (8/10)
    Alpha 冲刺 (7/10)
    Alpha 冲刺 (6/10)
    团 队 作 业 ———— 随 堂 小 测
    Alpha 冲刺 (5/10)
    Alpha 冲刺 (4/10)
    Beta冲刺博客汇总(麻瓜制造者)
    Beta冲刺(3/5)(麻瓜制造者)
    快速搭建一个Express工程骨架
    个人作业——软件产品案例分析
  • 原文地址:https://www.cnblogs.com/y4247464/p/10107552.html
Copyright © 2020-2023  润新知