zoukankan      html  css  js  c++  java
  • 七、设备驱动中的阻塞与非阻塞 IO(二)

    7.2 轮询

    7.2.1 介绍

      在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行。

      设备驱动程序中的轮询函数原型:

    1   /** 用于询问设备是否可以非阻塞的读写。当询问条件未触发时,用户空间进行 select() 和 poll() 将引起进程阻塞 */
    2     unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait);
    • wait:轮询表指针。
    • 这个函数应该进行两项工作:
      • 对可能引起设备文件状态变化的等待队列调用 poll_wait() 函数,将对应的等待队列头部添加到 poll_table 中
      • 返回表示是否能对设备进行无阻塞读、写访问的掩码

      

     7.2.2 globalfifo 支持轮询

     globalfifo.c

      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/mutex.h>
      8 #include <linux/wait.h>
      9 #include <linux/sched/signal.h> ///< 内核>5.0 使用
     10 //#include <linux/sched.h>
     11 #include <linux/poll.h>
     12 
     13 #define GLOBALFIFO_SIZE      0x1000
     14 #define MEM_CLEAR           0X1
     15 //#define GLOBALFIFO_MAGIC     'g'
     16 //#define MEM_CLEAR           _IO(GLOBALFIFO_MAGIC, 0)
     17 #define GLOBALFIFO_MAJOR    230
     18 #define DEVICE_NUMBER       10
     19 
     20 static int globalfifo_major = GLOBALFIFO_MAJOR;
     21 module_param(globalfifo_major, int, S_IRUGO);
     22 
     23 struct globalfifo_dev {
     24     struct cdev cdev;
     25     /** 
     26      *  目前 FIFO 中有效数据长度 
     27      *  current_len = 0, 表示 FIFO 为空
     28      *  current_len = GLOBALFIFO_SIZE, 表示 FIFO 满
     29      */
     30     unsigned int current_len;   
     31     unsigned char mem[GLOBALFIFO_SIZE];
     32     struct mutex mutex;
     33     wait_queue_head_t r_wait;   ///< 读等待队列头
     34     wait_queue_head_t w_wait;   ///< 写等待队列头
     35 };
     36 
     37 struct globalfifo_dev *globalfifo_devp;
     38 
     39 /** 
     40  * 这里涉及到私有数据的定义,大多数遵循将文件私有数据 pirvate_data 指向设备结构体,
     41  * 再用 read write llseek ioctl 等函数通过 private_data 访问设备结构体。
     42  * 对于此驱动而言,私有数据的设置是在 open 函数中完成的
     43  */
     44 static int globalfifo_open(struct inode *inode, struct file *filp)
     45 {
     46     /**
     47      *  NOTA: 
     48      *      container_of 的作用是通过结构体成员的指针找到对应结构体的指针。
     49      *      第一个参数是结构体成员的指针
     50      *      第二个参数是整个结构体的类型
     51      *      第三个参数为传入的第一个参数(即结构体成员)的类型
     52      *      container_of 返回值为整个结构体指针
     53      */ 
     54     struct globalfifo_dev *dev = container_of(inode->i_cdev, struct globalfifo_dev, cdev);
     55     filp->private_data = dev;
     56     return 0;
     57 }
     58 
     59 static int globalfifo_release(struct inode *inode, struct file *filp)
     60 {
     61     return 0;
     62 }
     63 
     64 static long globalfifo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
     65 {
     66     struct globalfifo_dev *dev = filp->private_data;
     67 
     68     switch(cmd){
     69     case MEM_CLEAR:
     70         mutex_lock(&dev->mutex);
     71         memset(dev->mem, 0, GLOBALFIFO_SIZE);
     72         printk(KERN_INFO "globalfifo is set to zero
    ");
     73         mutex_unlock(&dev->mutex);
     74         break;
     75     default:
     76         return -EINVAL;
     77     }
     78 
     79     return 0;
     80 }
     81 
     82 static unsigned int globalfifo_poll(struct file *filp, struct poll_table_struct *wait)
     83 {
     84     unsigned int mask = 0;
     85     struct globalfifo_dev *dev = filp->private_data;
     86 
     87     mutex_lock(&dev->mutex);
     88 
     89     poll_wait(filp, &dev->r_wait, wait);
     90     poll_wait(filp, &dev->w_wait, wait);
     91 
     92     if(dev->current_len != 0) {
     93         mask |= POLLIN | POLLRDNORM;
     94     }
     95 
     96     if(dev->current_len != GLOBALFIFO_SIZE){
     97         mask |= POLLIN | POLLWRNORM;
     98     }
     99 
    100     mutex_unlock(&dev->mutex);
    101     return mask;
    102 }
    103 
    104 
    105 static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig)
    106 {
    107     loff_t ret = 0;
    108     switch(orig) {
    109     case 0: /** 从文件开头位置 seek */
    110         if(offset < 0){
    111             ret = -EINVAL;
    112             break;
    113         }
    114         if((unsigned int)offset > GLOBALFIFO_SIZE){
    115             ret = -EINVAL;
    116             break;
    117         }
    118         filp->f_pos = (unsigned int)offset;
    119         ret = filp->f_pos;
    120         break;
    121     case 1: /** 从文件当前位置开始 seek */
    122         if((filp->f_pos + offset) > GLOBALFIFO_SIZE){
    123             ret = -EINVAL;
    124             break;
    125         }
    126         if((filp->f_pos + offset) < 0){
    127             ret = -EINVAL;
    128             break;
    129         }
    130         filp->f_pos += offset;
    131         ret = filp->f_pos;
    132         break;
    133     default:
    134         ret = -EINVAL;
    135         break;
    136     }
    137 
    138     return ret;
    139 }
    140 
    141 static ssize_t globalfifo_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
    142 {
    143     unsigned int count = size;
    144     int ret = 0;
    145     struct globalfifo_dev *dev = filp->private_data;
    146     
    147     DECLARE_WAITQUEUE(wait, current);   ///< 将当前进程加入到 wait 等待队列
    148     mutex_lock(&dev->mutex);
    149     add_wait_queue(&dev->w_wait, &wait);   ///< 添加等待队列元到读队列头中
    150 
    151     /** 判断设备是否可写 */
    152     while(dev->current_len == GLOBALFIFO_SIZE){
    153         /** 若是非阻塞访问, 设备忙时, 直接返回 -EAGAIN */
    154         if(filp->f_flags & O_NONBLOCK) {
    155             ret = -EAGAIN;
    156             goto out;
    157         }
    158         
    159         __set_current_state(TASK_INTERRUPTIBLE);    ///<改变进程状态为睡眠
    160         schedule();
    161         if(signal_pending(current)){    ///< 因为信号而唤醒
    162             ret = -ERESTARTSYS;
    163             goto out2;
    164         }
    165     }
    166 
    167     if(count > GLOBALFIFO_SIZE - dev->current_len)
    168         count = GLOBALFIFO_SIZE - dev->current_len;
    169     
    170     if(copy_from_user(dev->mem + dev->current_len, buf, count)){
    171         ret = -EFAULT;
    172         goto out;
    173     } else {
    174         dev->current_len += count;
    175         printk(KERN_INFO "written %u bytes(s), current len:%d
    ", count, dev->current_len);
    176 
    177         wake_up_interruptible(&dev->r_wait);    ///< 唤醒读等待队列
    178         ret = count;
    179     }
    180 out:
    181     mutex_unlock(&dev->mutex);
    182 out2:
    183     remove_wait_queue(&dev->w_wait, &wait); ///< 移除等待队列
    184     set_current_state(TASK_RUNNING);
    185     return ret;
    186 }
    187 
    188 /**
    189  * *ppos 是要读的位置相对于文件开头的偏移,如果该偏移大于或等于 GLOBALFIFO_SIZE,意味着已经独到文件末尾
    190  */
    191 static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
    192 {
    193     unsigned int count = size;
    194     int ret = 0;
    195     struct globalfifo_dev *dev = filp->private_data;
    196 
    197     DECLARE_WAITQUEUE(wait, current);   ///< 将当前进程加入到 wait 等待队列
    198     mutex_lock(&dev->mutex);
    199     add_wait_queue(&dev->r_wait, &wait);   ///< 添加等待队列元到读队列头中
    200 
    201     /** 等待 FIFO 非空,即判断设备是否可读 */
    202     while(dev->current_len == 0) {
    203         /** 若是非阻塞访问, 设备忙时, 直接返回 -EAGAIN */
    204         /** filp->f_flags 是用户空间 */
    205         if(filp->f_flags & O_NONBLOCK) {
    206             ret = -EAGAIN;
    207             goto out;
    208         }
    209 
    210         /** 
    211          *  阻塞访问,调度其他进程执行 
    212          *  FIFO 为空的情况下,读进程阻塞,必须依赖写进程往 FIFO 里面写东西唤醒它;
    213          *  但写的进程为了 FIFO,它必须拿到这个互斥体来访问 FIFO 这个临界资源;
    214          *  如果读进程把自己调度出去之前不释放这个互斥体,那么读写进程之间就死锁了
    215          */
    216         __set_current_state(TASK_INTERRUPTIBLE);    ///<改变进程状态为睡眠
    217         mutex_unlock(&dev->mutex);
    218         schedule();
    219         if(signal_pending(current)){    ///< 因为信号而唤醒
    220             ret = -ERESTARTSYS;
    221             goto out2;
    222         }
    223 
    224         mutex_lock(&dev->mutex);
    225     }
    226 
    227     /** 要读取的字节数大于设备文件中的有效数据长度 */
    228     if(count > dev->current_len)
    229         count = dev->current_len;
    230     
    231     /** 从用户空间拷贝数据 */
    232     if(copy_to_user(buf, dev->mem, count)) {
    233         ret = -EFAULT;
    234         goto out;
    235     } else {
    236         /** FIFO 中数据前移 */
    237         memcpy(dev->mem, dev->mem + count, dev->current_len - count);
    238         dev->current_len -= count;  ///< 有效数据长度减少
    239         printk(KERN_INFO "read %u bytes(s), current_len: %d
    ", count, dev->current_len);
    240 
    241         wake_up_interruptible(&dev->w_wait);    ///< 唤醒写等待队列
    242 
    243         ret = count;
    244     }
    245 out:
    246     mutex_unlock(&dev->mutex);
    247 out2:
    248     remove_wait_queue(&dev->r_wait, &wait); ///< 移除等待队列
    249     set_current_state(TASK_RUNNING);
    250     return ret;
    251 }
    252 
    253 static const struct file_operations globalfifo_fops = {
    254     .owner = THIS_MODULE,
    255     .poll = globalfifo_poll,
    256     .llseek = globalfifo_llseek,
    257     .read = globalfifo_read,
    258     .write = globalfifo_write,
    259     .unlocked_ioctl = globalfifo_ioctl,
    260     .open = globalfifo_open,
    261     .release = globalfifo_release,
    262 };
    263 
    264 
    265 /**
    266  * @brief  globalfifo_setup_cdev     
    267  *
    268  * @param  dev
    269  * @param  index    次设备号
    270  */
    271 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
    272 {
    273     int err;
    274     int devno = MKDEV(globalfifo_major, index);
    275 
    276     /** 使用 cdev_init 即是静态初始化了 cdev */
    277     cdev_init(&dev->cdev, &globalfifo_fops);
    278     dev->cdev.owner = THIS_MODULE;
    279 
    280     /** 设备编号范围设置为1,表示我们只申请了一个设备 */
    281     err = cdev_add(&dev->cdev, devno, 1);
    282     if(err)
    283         printk(KERN_NOTICE "Error %d adding globalfifo%d
    ", err, index);
    284 }
    285 
    286 static int __init globalfifo_init(void)
    287 {
    288     int ret;
    289     int i;
    290     dev_t devno = MKDEV(globalfifo_major, 0);
    291 
    292     if(globalfifo_major)
    293         ret = register_chrdev_region(devno, DEVICE_NUMBER, "globalfifo");
    294     else {
    295         ret = alloc_chrdev_region(&devno, 0, DEVICE_NUMBER, "globalfifo");
    296         globalfifo_major = MAJOR(devno);
    297     }
    298 
    299     if(ret < 0)
    300         return ret;
    301 
    302     globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
    303     if(!globalfifo_devp){
    304         ret = -ENOMEM;
    305         goto fail_malloc;
    306     }
    307 
    308     for(i = 0; i < DEVICE_NUMBER; i++){
    309         globalfifo_setup_cdev(globalfifo_devp + i, i);
    310     }
    311     
    312     mutex_init(&globalfifo_devp->mutex);
    313 
    314     /** 初始化读写等待队列 */
    315     init_waitqueue_head(&globalfifo_devp->r_wait);
    316     init_waitqueue_head(&globalfifo_devp->w_wait);
    317 
    318 fail_malloc:
    319     unregister_chrdev_region(devno, 1);
    320     return ret;
    321 }
    322 
    323 static void __exit globalfifo_exit(void)
    324 {
    325     int i;
    326     for(i = 0; i < DEVICE_NUMBER; i++) {
    327         cdev_del(&(globalfifo_devp + i)->cdev);
    328     }
    329     kfree(globalfifo_devp);
    330     unregister_chrdev_region(MKDEV(globalfifo_major, 0), DEVICE_NUMBER);
    331 }
    332 
    333 module_init(globalfifo_init);
    334 module_exit(globalfifo_exit);

    epoll_test.c

     1     }
     2 
     3     epfd = epoll_create(1);
     4     if(epfd < 0) {
     5         perror("epoll_create()");
     6         return ;
     7     }
     8 
     9     bzero(&ev_globalfifo, sizeof(struct epoll_event));
    10     ev_globalfifo.events = EPOLLIN | EPOLLPRI;
    11     err = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev_globalfifo);
    12     if(err < 0) {
    13         perror("epoll_ctl()");
    14         return ;
    15     }
    16     err = epoll_wait(epfd, &ev_globalfifo, 1, 15000);
    17     if(err < 0) {
    18         perror("epoll_wait()");
    19     } else if(err == 0) {
    20         printf("No data input in FIFO within 15 seconds.
    ");
    21     } else {
    22         printf("FIFO is not empty.
    ");
    23     }
    24 
    25     err = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev_globalfifo);
    26     if(err < 0) {
    27         perror("epoll_ctl()");
    28         return ;
    29     }
    30 
    31     return;
    32 
    33 }

    Makefile

     1 .PHONY:all clean test
     2 
     3 TEST_SRC := poll_test.c
     4 TEST_TARGET := poll_test
     5 
     6 ifneq ($(KERNELRELEASE),)
     7 
     8 obj-m := globalfifo.o
     9 
    10 else
    11 LINUX_KERNEL := $(shell uname -r)
    12 LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
    13 CURRENT_PATH  := $(shell pwd)
    14 EXTRA_CFLAGS += -DDEBUG
    15 
    16 all:
    17     make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
    18 clean:
    19     rm -fr *.ko *.o *.mod.o *.mod.c *.symvers *.order .*.ko *.cmd .tmp_versions $(TEST_TARGET)
    20 
    21 test:
    22     gcc -o $(TEST_TARGET) $(TEST_SRC)
    23 endif
  • 相关阅读:
    Intellij Idea开发工具在@Autowired或者@Resource注入XxxMapper接口时报如下错误Could not autowire. No beans of 'TbItemMapper' type found
    java.lang.IllegalStateException: getOutputStream() has already been called for this response解决方案
    Javaweb项目配置到阿里云服务器
    VMware15.5.0安装MacOS10.15.0系统 安装步骤(上)
    单元测试 springboot-test
    虚拟机字节码指令表
    tomcat服务器源码解读02-基本原理
    tomcat服务器源码解读01-整体结构
    Elasticsearch kibana官方基础本地实践
    虚拟机部署单机版kubernetes,minikube,docker
  • 原文地址:https://www.cnblogs.com/kele-dad/p/11693698.html
Copyright © 2011-2022 走看看