• Linux之poll机制分析


    应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open()、read()、write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直

    到被唤醒。如果是使用非阻塞方式,就需要采用poll/select机制,而且打开文件时标记文件的访问权限位为O_NONBLOCK。

    1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 

    FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位

    FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真

    FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位

    FD_ZERO(fd_set *set);用来清除描述词组set的全部位

    如果参数timeout设为:NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

    特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

    1 int poll(struct pollfd *fds, nfds_t nfds, int timeout);这两个函数其实本质类似.

    fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回

      struct pollfd {

        int fd; /* 文件描述符 */

        short events; /* 请求的事件类型,监视驱动文件的事件掩码 */

        short revents; /* 驱动文件实际返回的事件 */

      } ;

    nfds 监测驱动文件的个数

    timeout 超时时间,单位为ms

    事件类型events 可以为下列值:

    POLLIN 有数据可读

    POLLRDNORM 有普通数据可读,等效与POLLIN

    POLLPRI 有紧迫数据可读

    POLLOUT 写数据不会导致阻塞

    POLLER 指定的文件描述符发生错误

    POLLHUP 指定的文件描述符挂起事件

    POLLNVAL 无效的请求,打不开指定的文件描述符

    返回值

    有事件发生 返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告)

    超时        返回0;

    失败   返回-1,并设置errno为错误类型

    理解select模型:

    理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每个bit 可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

    (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

    (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

    (3)若再加入fd=2,fd=1,则set变为0001,0011

    (4)执行select(6,&set,0,0,0)阻塞等待

    (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。

    注意:没有事件 发生的fd=5被清空。

    从内核态理解poll机制

    我们从应用程序直接调用poll函数,系统会走以下流程

     1 app:poll
     2 kernel:sys_poll
     3             do_sys_poll
     4                 poll_initwait(&table)
     5                 do_poll(nfds, head, &table, timeout)
     6                         for (;;) {
     7                             for (; pfd != pfd_end; pfd++) {    /* 可以监测多个驱动设备所产生的事件 */
     8                                 if (do_pollfd(pfd, pt)) {   
     9                                     count++;
    10                                     pt = NULL;
    11                                 }
    12                                 if (count || !*timeout || signal_pending(current))
    13                                     break;
    14                                 __timeout = schedule_timeout(__timeout);
    15                             }
    16                         }
    17 
    18 
    19 do_pollfd(pfd, pt){
    20 ...
    21 if (file->f_op && file->f_op->poll)
    22     mask = file->f_op->poll(file, pwait);
    23     return mask;                
    24 ...
    25 }

    使用poll_initwait(&table),就是将__pollwait设为回调函数,后面会去调用驱动程序的poll函数,poll函数调用pollwait就等于调用__pollwait,将当前进程加入到等待队列中。然后一直在循环,do_pollfd就是去

    调用驱动程序的poll函数,poll函数开始调用pollwait就等于调用__pollwait回调函数,将当前进程加入到等待队列中,以便唤醒休眠后的当前进程。然后返回当前驱动设备的状态(mask). 如果do_pollfd返回的

    mask为非0,即count非0,就会马上返回,应用程序就可以使用FD_ISSET了解此时设备状态。当然,如果超时或者此进程有其他信号要处理,也会马上返回,但是应用程序使用FD_ISSET了解到此时设备状

    态还是不可用时,又继续轮询。如果do_pollfd返回的mask为0,而且未超时且未有其他信号发生,就会进程调度,让此进程休眠。在前面已经将此进程加入到驱动程序的等待队列中了,如果设备可用时,就会

    唤醒等待队列中的进程,也就唤醒了此进程,又去do_pollfd(pfd, pt)。

    实例:基于<<Linux设备驱动开发详解:基于最新的Linux4.0内核.pdf>>第8.2章节

    驱动程序的poll函数

     1 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
     2  {
     3       unsigned int mask =0;
     4       struct globalfifo_dev *dev = filp->private_data;
     5   
     6       mutex_lock(&dev->mutex);
     7   
     8       poll_wait(filp, &dev->w_wait, wait);//将当前进程加入到写等待队列 
     9       poll_wait(filp, &dev->r_wait, wait); //将当前进程加入到读等待队列 
    10  
    11      if(dev->current_len != 0)
    12          mask |=POLLIN | POLLRDNORM;//当有数据时,报告可读状态
    13      if(dev->current_len != GLOBALMEM_SIZE)
    14          mask |=POLLOUT | POLLWRNORM;//当缓冲区未满时,报告可写状态
    15  
    16      mutex_unlock(&dev->mutex);
    17      return mask;
    18  }

    使用select监控globalfifo是否可非阻塞读、 写的应用程序

     1 #include "stdio.h"
     2 #include <sys/types.h>
     3 #include <sys/stat.h>
     4 #include <fcntl.h>
     5 #include <sys/time.h>
     6 #include <unistd.h>
     7 #include <errno.h>
     8 #define FIFO_CLEAR (0x01)
     9 void main(int argc, char **argv)
    10 {
    11     int fd;
    12     int err;
    13     fd_set rfds,wfds;
    14     struct timeval timeout;
    15     timeout.tv_sec = 5; //设置超时时间为5s
    16 
    17     fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
    18     if(fd == -1)
    19         printf("open fail
    ");
    20     else{
    21          if (ioctl(fd, FIFO_CLEAR, 0) < 0)
    22              printf("ioctl command failed
    ");
    23          while(1){
    24             FD_ZERO(&rfds);
    25             FD_ZERO(&wfds);
    26             FD_SET(fd,&rfds);
    27             FD_SET(fd,&wfds);
    28 
    29             err = select(fd+1, &rfds, &wfds, NULL, &timeout);
    30             if(err == -1)
    31                 printf("select fail:0x%x
    ",errno);
    32             if(FD_ISSET(fd, &rfds))
    33                 printf("Poll monitor:can be read
    ");
    34             if(FD_ISSET(fd, &wfds))
    35                 printf("Poll monitor:can be write
    ");
    36          }
    37     }
    38     return 0;
    39 }

    驱动程序全部源码:

      1 #include <linux/module.h>
      2 #include <linux/fs.h>
      3 #include <linux/init.h>
      4 #include <linux/cdev.h>
      5 #include <linux/slab.h>
      6 #include <linux/uaccess.h>
      7 #include <linux/poll.h>
      8 
      9 //#define GLOBALMEM_SIZE 0x1000
     10 #define GLOBALMEM_SIZE 0x10
     11 #define GLOBALMEM_MAJOR 230
     12 #define GLOBALMEM_MAGIC 'g'
     13 //#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0)
     14 #define MEM_CLEAR (0x01)
     15 static int globalfifo_major = GLOBALMEM_MAJOR;
     16 module_param(globalfifo_major, int, S_IRUGO);
     17 
     18 struct globalfifo_dev {
     19  struct cdev cdev;
     20  unsigned int current_len;
     21  unsigned char mem[GLOBALMEM_SIZE];
     22  struct mutex mutex;
     23  wait_queue_head_t r_wait;
     24  wait_queue_head_t w_wait;
     25 };
     26 
     27 struct globalfifo_dev *globalfifo_devp;
     28 
     29 static int globalfifo_open(struct inode *inode, struct file *filp)
     30 {
     31     filp->private_data = globalfifo_devp;
     32     return 0;
     33 }
     34 static int globalfifo_release(struct inode *inode, struct file *filp)
     35 {
     36     return 0;
     37 }
     38 static ssize_t globalfifo_read(struct file *filp, char __user * buf, size_t size,
     39  loff_t * ppos)
     40 {
     41     unsigned int count = size;
     42     int ret = 0;
     43     struct globalfifo_dev *dev = filp->private_data;
     44     DECLARE_WAITQUEUE(wait, current);
     45 
     46     mutex_lock(&dev->mutex);
     47     add_wait_queue(&dev->r_wait, &wait);
     48 
     49     while(dev->current_len ==0){
     50         if(filp->f_flags & O_NONBLOCK){
     51             ret = -EAGAIN;
     52             goto out;
     53         }
     54 
     55         set_current_state(TASK_INTERRUPTIBLE);
     56         mutex_unlock(&dev->mutex);
     57 
     58         schedule();
     59         if(signal_pending(current)){
     60             ret = -ERESTARTSYS;
     61             goto out2;
     62         }
     63         mutex_lock(&dev->mutex);
     64 
     65     }
     66 
     67     if (count > dev->current_len)
     68         count = dev->current_len;
     69 
     70     if (copy_to_user(buf, dev->mem, count)) {
     71         ret = -EFAULT;
     72         goto out;
     73     } else {
     74         memcpy(dev->mem, dev->mem+count, dev->current_len - count);
     75         dev->current_len -=count;
     76         printk(KERN_INFO "read %d bytes(s) current_len %d
    ", count, dev->current_len);
     77         wake_up_interruptible(&dev->w_wait);
     78         ret = count;
     79     }
     80 
     81 out:
     82     mutex_unlock(&dev->mutex);
     83 out2:
     84     remove_wait_queue(&dev->r_wait, &wait);
     85     set_current_state(TASK_RUNNING);
     86 
     87  return ret;
     88 }
     89 
     90 static ssize_t globalfifo_write(struct file *filp, const char __user * buf,
     91  size_t size, loff_t * ppos)
     92 {
     93     unsigned int count = size;
     94     int ret = 0;
     95     struct globalfifo_dev *dev = filp->private_data;
     96     DECLARE_WAITQUEUE(wait, current);
     97 
     98     mutex_lock(&dev->mutex);
     99     add_wait_queue(&dev->w_wait, &wait);
    100 
    101     while(dev->current_len == GLOBALMEM_SIZE){
    102         if(filp->f_flags & O_NONBLOCK){
    103             ret = -EAGAIN;
    104             goto out;
    105         }
    106 
    107         set_current_state(TASK_INTERRUPTIBLE);
    108         mutex_unlock(&dev->mutex);
    109         schedule();
    110         if(signal_pending(current)){
    111             ret = -ERESTARTSYS;
    112             goto out2;
    113         }
    114         mutex_lock(&dev->mutex);
    115 
    116     }
    117 
    118     if (count > (GLOBALMEM_SIZE - dev->current_len))
    119         count = (GLOBALMEM_SIZE - dev->current_len);
    120 
    121     if (copy_from_user(dev->mem + dev->current_len, buf, count)){
    122         ret = -EFAULT;
    123         goto out;
    124     }
    125     else {
    126         dev->current_len += count;
    127         wake_up_interruptible(&dev->r_wait);
    128         ret = count;
    129         printk(KERN_INFO "written %d bytes(s) current_len %d
    ", count, dev->current_len);
    130     }
    131 out:
    132     mutex_unlock(&dev->mutex);
    133 out2:
    134     remove_wait_queue(&dev->w_wait, &wait);
    135     set_current_state(TASK_RUNNING);
    136  return ret;
    137 }
    138 static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig)
    139 {
    140     loff_t ret = 0;
    141     switch (orig) {
    142     case 0: /* ´ÓÎļþ¿ªÍ·Î»ÖÃseek */
    143     if (offset< 0) {
    144         ret = -EINVAL;
    145         break;
    146     }
    147     if ((unsigned int)offset > GLOBALMEM_SIZE) {
    148         ret = -EINVAL;
    149         break;
    150     }
    151     filp->f_pos = (unsigned int)offset;
    152     ret = filp->f_pos;
    153     break;
    154     case 1: /* ´ÓÎļþµ±Ç°Î»ÖÿªÊ¼seek */
    155     if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
    156         ret = -EINVAL;
    157         break;
    158     }
    159     if ((filp->f_pos + offset) < 0) {
    160         ret = -EINVAL;
    161         break;
    162     }
    163     filp->f_pos += offset;
    164     ret = filp->f_pos;
    165     break;
    166     default:
    167     ret = -EINVAL;
    168     break;
    169     }
    170     return ret;
    171 }
    172 static long globalfifo_ioctl(struct file *filp, unsigned int cmd,
    173  unsigned long arg)
    174 {
    175     struct globalfifo_dev *dev = filp->private_data;
    176     switch (cmd) {
    177     case MEM_CLEAR:
    178         mutex_lock(&dev->mutex);
    179         memset(dev->mem, 0, GLOBALMEM_SIZE);
    180     dev->current_len =0;
    181         printk(KERN_INFO "globalfifo is set to zero
    ");
    182         mutex_unlock(&dev->mutex);
    183         break;
    184     default:
    185     return -EINVAL;
    186  }
    187 
    188  return 0;
    189 }
    190 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
    191 {
    192     unsigned int mask =0;
    193     struct globalfifo_dev *dev = filp->private_data;
    194 
    195     mutex_lock(&dev->mutex);
    196 
    197     poll_wait(filp, &dev->w_wait, wait);
    198     poll_wait(filp, &dev->r_wait, wait);
    199 
    200     if(dev->current_len != 0)
    201         mask |=POLLIN | POLLRDNORM;
    202     if(dev->current_len != GLOBALMEM_SIZE)
    203         mask |=POLLOUT | POLLWRNORM;
    204 
    205     mutex_unlock(&dev->mutex);
    206     return mask;
    207 }
    208 
    209 
    210 static const struct file_operations globalfifo_fops = {
    211  .owner          = THIS_MODULE,
    212  .llseek         = globalfifo_llseek,
    213  .read           = globalfifo_read,
    214  .write          = globalfifo_write,
    215  .unlocked_ioctl = globalfifo_ioctl,
    216  .open           = globalfifo_open,
    217  .poll           = globalfifo_poll,
    218  .release        = globalfifo_release,
    219 };
    220 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
    221 {
    222     int err, devno = MKDEV(globalfifo_major, index);
    223     cdev_init(&dev->cdev, &globalfifo_fops);
    224     dev->cdev.owner = THIS_MODULE;
    225     err = cdev_add(&dev->cdev, devno, 1);
    226     if (err)
    227     printk(KERN_NOTICE "Error %d adding globalfifo%d", err, index);
    228 }
    229 static int __init globalfifo_init(void)
    230 {
    231     int ret;
    232     dev_t devno = MKDEV(globalfifo_major, 0);
    233 
    234     if (globalfifo_major)
    235     ret = register_chrdev_region(devno, 1, "globalfifo");
    236     else {
    237     ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    238     globalfifo_major = MAJOR(devno);
    239     }
    240     if (ret < 0)
    241     return ret;
    242 
    243     globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
    244     if (!globalfifo_devp) {
    245     ret = -ENOMEM;
    246     goto fail_malloc;
    247     }
    248     globalfifo_setup_cdev(globalfifo_devp, 0);
    249 
    250     mutex_init(&globalfifo_devp->mutex);
    251     init_waitqueue_head(&globalfifo_devp->r_wait);
    252     init_waitqueue_head(&globalfifo_devp->w_wait);
    253     return 0;
    254 
    255 fail_malloc:
    256     unregister_chrdev_region(devno, 1);
    257     return ret;
    258 }
    259 
    260 static void __exit globalfifo_exit(void)
    261 {
    262     cdev_del(&globalfifo_devp->cdev);
    263     kfree(globalfifo_devp);
    264     unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1);
    265 }
    266 module_init(globalfifo_init);
    267 module_exit(globalfifo_exit);
    268 
    269 MODULE_LICENSE("GPL v2");
    View Code

    测试:将应用程序设置为后台执行。

    当无数据时.

    当有数据时但未满时.

    当数据满时.

    此文源码基于内核源码版本为linux-2.6.22.6

    参考:https://www.cnblogs.com/amanlikethis/p/6915485.html

              http://www.cnblogs.com/shihaochangeworld/p/5747490.html

  • 相关阅读:
    修改MySQL表varchar字段的小实验
    MySQL5.6同步指定数据库
    Redis安装
    MongoDB安装
    RMAN备份注意事项
    Oracle Data Guard Protection Modes
    Linux Shell 文本处理工具
    ORACLE 收缩表空间的数据文件
    crfclust.bdb导致磁盘满
    undo管理
  • 原文地址:https://www.cnblogs.com/hwli/p/9218245.html
Copyright © 2020-2023  润新知