字符驱动学习二(阻塞I/O)

原创 2016年08月31日 12:10:25
     上次调试好了一个基本的字符驱动,支持并发控制。在实际的使用中,read 和 write 系统调用要支持阻塞访问的。这次使用等待队列实现阻塞。在实现阻塞的代码中涉及进程调度的内容。下面是read和write 函数:

static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    62    {
    63        int ret = 0;
    64        struct cfifo_t *dev = filp->private_data;
    65        DECLARE_WAITQUEUE(wait, current);
    66    
    67        down(&dev->sem);
    68        add_wait_queue(&dev->r_wait, &wait);
    69    
    70        while(dev->current_len == 0){
    71            if(filp->f_flags & O_NONBLOCK){
    72                ret = -EAGAIN;
    73                goto out;
    74            }
    75    
    76            __set_current_state(TASK_INTERRUPTIBLE);
    77            up(&dev->sem);
    78    
    79            schedule(); /*调度其他进程运行*/
    80            if(signal_pending(current)){
    81                ret = -ERESTARTSYS;
    82                goto out2;
    83            }
    84    
    85            down(&dev->sem);
    86        }
    87    
    88        if(count > dev->current_len)
    89            count = dev->current_len;
    90    
    91        if(copy_to_user(buf, (void*)(dev->mem), count)){
    92            ret = -EFAULT;
    93            goto out;
    94        } else{
    95            memcpy(dev->mem, dev->mem + count, dev->current_len - count);
    96            dev->current_len -= count;
    97            *fpos =dev->current_len;
    98    
    99            wake_up_interruptible(&dev->w_wait);
   100            ret =count;
   101    
   102            printk(KERN_INFO "read %s %d bites \n", buf, count);
   103        }
   104    
   105    out:
   106        up(&dev->sem);
   107    out2:
   108        remove_wait_queue(&dev->r_wait, &wait);
   109        set_current_state(TASK_RUNNING);
   110    
   111        return ret;
   112    }
   113
static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
   115    {
   116        int ret = 0;
   117        struct cfifo_t *dev = filp->private_data;
   118        DECLARE_WAITQUEUE(wait, current);
   119    
   120        down(&dev->sem);
   121        add_wait_queue(&dev->w_wait, &wait);
   122    
   123        while(dev->current_len >= GLOBALMEM_SIZE){
   124            if(filp->f_flags & O_NONBLOCK){                125                ret = -EAGAIN;
   126                goto out;
   127            }
   128    
   129            __set_current_state(TASK_INTERRUPTIBLE);    /* 进程状态,定义在 linux/sched.h */
   130            up(&dev->sem);                              /* 释放信号量,避免死锁 */
   131    
   132            schedule();                                 /* 调度其他进程运行*/
   133            if(signal_pending(current)){                
   134                ret = -ERESTARTSYS;
   135                goto out2;
   136            }
   137    
   138            down(&dev->sem);
   139        }
   140    
   141        if(count > GLOBALMEM_SIZE - dev->current_len)
   142            count = GLOBALMEM_SIZE - dev->current_len;
   143    
   144    
   145        if(copy_from_user(dev->mem+dev->current_len, buf, count)){
   146            ret = - EFAULT;
   147            goto out;
   148        }
   149        else{
   150            dev->current_len += count;
   151            *fpos = dev->current_len;
   152            ret = count;
   153    
   154            wake_up_interruptible(&dev->r_wait);
   155            printk(KERN_INFO "write %s %d bites \n", buf, count);
   156        }
   157    
   158    out:
   159        up(&dev->sem);
   160    out2:
   161        remove_wait_queue(&dev->w_wait, &wait);
   162        set_current_state(TASK_RUNNING);
   163    
   164        return ret;
   165    }
  开始读,这时设备中没有内容,read函数会阻塞在79行。然后向设备中写入内容,write函数运行到154行时,会唤醒写的进程和65行代码有关

  1. DECLARE_WAITQUEUE(wait, current)
下面是验证:
  1. root@wang:/work/wanghuan/drives# ls
  2. cdev.c cfifo.c cfifo.ko Makefile modules.order
  3. root@wang:/work/wanghuan/drives# insmod cfifo.ko
  4. root@wang:/work/wanghuan/drives# ls /dev/cfifo*
  5. /dev/cfifo0 /dev/cfifo1
  6. root@wang:/work/wanghuan/drives# cat /dev/cfifo0 &
  7. [1] 2889
  8. root@wang:/work/wanghuan/drives# ps
  9.   PID TTY TIME CMD
  10.  2046 pts/0 00:00:00 bash
  11.  2889 pts/0 00:00:00 cat
  12.  2890 pts/0 00:00:00 ps
  13. root@wang:/work/wanghuan/drives# ls > /dev/cfifo0
  14. cdev.c
  15. cfifo.c
  16. cfifo.ko
  17. Makefile
  18. modules.order
  19. root@wang:/work/wanghuan/drives# ls > /dev/cfifo1
  20. cdev.c
  21. cfifo.c
  22. cfifo.ko
  23. Makefile
  24. modules.order
  25. root@wang:/work/wanghuan/drives
下面是完整的源码:
  1. /**
  2.  * =====================================================================================
  3.  * Filename: cfifo.c
  4.  * Description: 字符设备驱动模型 阻塞I/O
  5.  * Version: 1.0
  6.  * Created: 2011年06月12日 17时19分50秒
  7.  * Revision: none
  8.  * Compiler: gcc
  9.  *
  10.  * Author: wanghuan,
  11.  * Company:
  12.  * =====================================================================================
  13.  */

  14. #include <linux/module.h>
  15. #include <linux/types.h>
  16. #include <linux/fs.h>         /* file_operation */
  17. #include <linux/errno.h>     /* Error number */
  18. #include <linux/mm.h>    
  19. #include <linux/sched.h>    
  20. #include <linux/slab.h>    
  21. #include <linux/init.h>     /* __init __exit */
  22. #include <linux/device.h>
  23. #include <linux/cdev.h>    
  24. #include <asm/io.h>    
  25. #include <asm/system.h>    
  26. #include <asm/uaccess.h>     /* copy_to_user, copy_from_user */
  27. #include <linux/kernel.h>     /* printk() */

  28. #define GLOBALMEM_SIZE 0x1000
  29. #define MEM_CLEAN 0x1
  30. #define DEV_NAME "cfifo"
  31. #define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)

  32. static int MAJOR_NR = 255;        /* Driver Major Number */
  33. //static int MINOR_NR = 0;        /* Driver Major Number */

  34. struct cfifo_t{
  35.     struct cdev cdev;
  36.     unsigned char mem[GLOBALMEM_SIZE];
  37.     struct semaphore sem;

  38.     unsigned int current_len;
  39.     wait_queue_head_t r_wait;
  40.     wait_queue_head_t w_wait;
  41. };
  42. static struct cfifo_t *cfifo_p;

  43. static int cfifo_open(struct inode *inode, struct file *filp)
  44. {
  45.     struct cfifo_t *dev;
  46.     dev = container_of(inode->i_cdev, struct cfifo_t, cdev);
  47.     filp->private_data = dev;
  48.     return 0;
  49. }
  50. static int cfifo_release(struct inode *inode, struct file *filp)
  51. {
  52.     filp->private_data = NULL;
  53.     return 0;
  54. }
  55. static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  56. {
  57.     int ret = 0;
  58.     struct cfifo_t *dev = filp->private_data;
  59.     DECLARE_WAITQUEUE(wait, current);
  60.     
  61.     down(&dev->sem);
  62.     add_wait_queue(&dev->r_wait, &wait);

  63.     while(dev->current_len == 0){
  64.         if(filp->f_flags & O_NONBLOCK){
  65.             ret = -EAGAIN;
  66.             goto out;
  67.         }

  68.         __set_current_state(TASK_INTERRUPTIBLE);
  69.         up(&dev->sem);

  70.         schedule(); /*调度其他进程运行*/
  71.         if(signal_pending(current)){
  72.             ret = -ERESTARTSYS;
  73.             goto out2;
  74.         }

  75.         down(&dev->sem);
  76.     }

  77.     if(count > dev->current_len)
  78.         count = dev->current_len;

  79.     if(copy_to_user(buf, (void*)(dev->mem), count)){
  80.         ret = -EFAULT;
  81.         goto out;
  82.     } else{
  83.         memcpy(dev->mem, dev->mem + count, dev->current_len - count);
  84.         dev->current_len -= count;
  85.         *fpos =dev->current_len;

  86.         wake_up_interruptible(&dev->w_wait);
  87.         ret =count;

  88.         printk(KERN_INFO "read %s %d bites \n", buf, count);
  89.     }

  90. out:
  91.     up(&dev->sem);
  92. out2:
  93.     remove_wait_queue(&dev->r_wait, &wait);
  94.     set_current_state(TASK_RUNNING);

  95.     return ret;
  96. }

  97. static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  98. {
  99.     int ret = 0;
  100.     struct cfifo_t *dev = filp->private_data;
  101.     DECLARE_WAITQUEUE(wait, current);

  102.     down(&dev->sem);
  103.     add_wait_queue(&dev->w_wait, &wait);

  104.     while(dev->current_len >= GLOBALMEM_SIZE){
  105.         if(filp->f_flags & O_NONBLOCK){
  106.             ret = -EAGAIN;
  107.             goto out;
  108.         }

  109.         __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
  110.         up(&dev->sem); /* 释放信号量,避免死锁 */

  111.         schedule(); /* 调度其他进程运行*/
  112.         if(signal_pending(current)){
  113.             ret = -ERESTARTSYS;
  114.             goto out2;
  115.         }

  116.         down(&dev->sem);
  117.     }

  118.     if(count > GLOBALMEM_SIZE - dev->current_len)
  119.         count = GLOBALMEM_SIZE - dev->current_len;


  120.     if(copy_from_user(dev->mem+dev->current_len, buf, count)){
  121.         ret = - EFAULT;
  122.         goto out;
  123.     }
  124.     else{
  125.         dev->current_len += count;
  126.         *fpos = dev->current_len;
  127.         ret = count;

  128.         wake_up_interruptible(&dev->r_wait);
  129.         printk(KERN_INFO "write %s %d bites \n", buf, count);
  130.     }

  131. out:
  132.     up(&dev->sem);
  133. out2:
  134.     remove_wait_queue(&dev->w_wait, &wait);
  135.     set_current_state(TASK_RUNNING);

  136.     return ret;
  137. }

  138. static loff_t cfifo_llseek(struct file *filp, loff_t offset, int orig)
  139. {
  140.     loff_t ret;
  141.     switch(orig){
  142.         case 0:
  143.             if(offset < 0){
  144.                 ret = -EFAULT;
  145.                 break;
  146.             }
  147.             if((unsigned int)offset > GLOBALMEM_SIZE){
  148.                 ret = -EFAULT;
  149.                 break;
  150.             }

  151.             filp->f_pos = (unsigned int)offset;
  152.             ret = filp->f_pos;
  153.             break;
  154.         case 1:
  155.             if(filp->f_pos + offset > GLOBALMEM_SIZE){
  156.                 ret = -EFAULT;
  157.                 break;
  158.             }

  159.             if(filp->f_pos + offset < 0){
  160.                 ret = -EFAULT;
  161.                 break;
  162.             }

  163.             filp->f_pos += offset;
  164.             ret = filp->f_pos;
  165.             break;
  166.         default:
  167.             ret = -EFAULT;
  168.     }
  169.     return ret;
  170. }

  171. static int cfifo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  172. {
  173.     struct cfifo_t *dev = filp->private_data;
  174.     switch(cmd){
  175.         case MEM_CLEAR:
  176.             if(down_interruptible(&dev->sem))
  177.                 return -ERESTARTSYS;

  178.             memset(dev->mem, 0, GLOBALMEM_SIZE);

  179.             up(&dev->sem);
  180.             printk(KERN_INFO "globalmem is set to zero \n");
  181.             break;
  182.         default:
  183.             return -EINVAL;
  184.     }

  185.     return 0;
  186. }

  187. static const struct file_operations cfifo_fops = {
  188.     .owner = THIS_MODULE,
  189.     .open = cfifo_open,
  190.     .release = cfifo_release,
  191.     .read = cfifo_read,
  192.     .write = cfifo_write,
  193.     .ioctl = cfifo_ioctl,
  194.     .llseek = cfifo_llseek,
  195. };

  196. static void cfifo_setup_cdev(struct cfifo_t *cfifo_cdev, int minor)
  197. {
  198.     int err;
  199.     dev_t devno = MKDEV(MAJOR_NR, minor);
  200.     
  201.     cdev_init(&cfifo_cdev->cdev, &cfifo_fops);
  202.     cfifo_p->cdev.owner = THIS_MODULE;
  203.     err = cdev_add(&cfifo_cdev->cdev, devno, 1);
  204.     if(err != 0)
  205.         printk(KERN_NOTICE "Error %d adding gmen", err);

  206.     cfifo_cdev->current_len = 0;
  207.     init_MUTEX(&cfifo_cdev->sem);
  208.     init_waitqueue_head(&cfifo_cdev->r_wait);
  209.     init_waitqueue_head(&cfifo_cdev->w_wait);
  210. }

  211. static struct class *cdev_class;
  212. static int __init cfifo_cdev_init(void)
  213. {
  214.     int result;
  215.     dev_t devno = MKDEV(MAJOR_NR, 0);

  216.     if(0 != MAJOR_NR){
  217.         result = register_chrdev_region(devno, 1, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
  218.     } else {
  219.         result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
  220.         MAJOR_NR = MAJOR(devno);
  221.     }

  222.     printk(KERN_CRIT"hello cfifo\n");
  223.     if(result < 0)
  224.         return result;

  225.     cfifo_p = kmalloc(2*sizeof(struct cfifo_t), GFP_KERNEL);
  226.     if(NULL == cfifo_p){
  227.         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1);
  228.         result = -ENOMEM;
  229.     }

  230.     memset(cfifo_p, 0, 1*sizeof(struct cfifo_t));
  231.     cfifo_setup_cdev(&cfifo_p[0], 0);
  232.     cfifo_setup_cdev(&cfifo_p[0], 1);

  233.     cdev_class = class_create(THIS_MODULE, DEV_NAME);
  234.     if(IS_ERR(cdev_class))
  235.         return PTR_ERR(cdev_class);

  236.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0);
  237.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1);
  238.     return 0;
  239. }

  240. void cfifo_cdev_exit(void)
  241. {
  242.     cdev_del(&cfifo_p[0].cdev);
  243.     cdev_del(&cfifo_p[0].cdev);

  244.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
  245.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
  246.     class_destroy(cdev_class); //delete class created by us

  247.     kfree(cfifo_p);
  248.     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1); //由于注册了两个设备,最后一个参数为 2
  249. }

  250. module_init(cfifo_cdev_init);
  251. module_exit(cfifo_cdev_exit);
  252. MODULE_LICENSE("GPL");
  253. MODULE_AUTHOR("wanghuan");

Makefile:
  1. #===============================================================================
  2. # Filename: Makefile
  3. # Description:
  4. #
  5. # Author: wanghuan
  6. # Company:
  7. #
  8. #==============================================================================

  9. obj-m :=cfifo.o #目标文件
  10. KDIR :=/lib/modules/2.6.35-22-generic/build #内核路径
  11. PWD := $(shell pwd) #模块源文件路径
  12. all:    
  13.     $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
  14.     @rm -rf *.mod.*
  15.     @rm -rf .*.cmd
  16.     @rm -rf *.o
  17.     @rm -rf Module.*
  18.     chmod a+x cfifo.ko
  19. clean:

版权声明:本文为博主原创文章,转载请注明出处。

Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O

在前面学习网络编程时,曾经学过I/O模型
  • zqixiao_09
  • zqixiao_09
  • 2016年03月15日 21:04
  • 2046

深入浅出:Linux设备驱动中的阻塞和非阻塞I/O

今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。一、基本概念:阻塞操作 : ...
  • soonfly
  • soonfly
  • 2016年09月20日 17:57
  • 527

操作系统I/O:阻塞和非阻塞

操作系统内核对于I/O只有两种方式:阻塞和非阻塞。 调用阻塞I/O时,应用程序需要等待I/O完成才返回结果,阻塞I/O的一个特点是调用之后一定要等到系统内核层面完成所有操作后,调用才结束。 调用非...
  • u014744118
  • u014744118
  • 2017年01月15日 20:09
  • 705

五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O

五种I/O 模式: 【1】        阻塞 I/O           (Linux下的I/O操作默认是阻塞I/O,即open和socket创建的I/O都是阻塞I/O) 【2】        ...
  • suwei19870312
  • suwei19870312
  • 2014年03月07日 17:04
  • 1226

I/O模型:同步I/O和异步I/O,阻塞I/O和非阻塞I/O

同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别? 这个问题其实不同...
  • shenlei19911210
  • shenlei19911210
  • 2015年10月21日 16:04
  • 637

异步式I/O与事件驱动--起步

Node.js使用的是单线程模型,对于所有I/O都采用异步式的请求方式,避免了频繁的上下文切换。Node.js在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式I...
  • Saya_gt
  • Saya_gt
  • 2015年11月25日 22:38
  • 759

nodejs 异步I/O和事件驱动

nodejs 异步IO和事件驱动 几个例子 example 1 example 2 example 3 example 4 example 5 异步IOasynchronous IO 阻塞IO 和 非...
  • ii1245712564
  • ii1245712564
  • 2016年05月22日 09:51
  • 7460

3. node.js 异步式I/O或非阻塞式I/O

Node.js最大的特性就是异步式I/O与事件紧密结合的编程模式。这种模式与传统的同步式IO线性的编程思路有很大的不同,因为控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元格。 ...
  • silvercell
  • silvercell
  • 2016年07月07日 12:49
  • 677

Linux环境编程之高级I/O(一):非阻塞I/O、记录锁

引言:高级I/O包括非阻塞I/O、记录锁、系统V流机制、I/O多路转接(select和poll函数)、readv和writev函数以及存储映射I/O。 (一)非阻塞I/O 可能会使进程永远阻塞的一...
  • To_Be_IT_1
  • To_Be_IT_1
  • 2014年05月18日 14:00
  • 1137

Linux信号驱动I/O 学习记录

Q:什么是信号驱动I/O? A:对于给定的I/O口(一般就是对于文件描述符)设定为信号驱动I/O,则当I/O口准备好之后(读:有数据可读;写:有空间可写),向注册它的进程发送事先约定好的信号,进程收...
  • McHeaven
  • McHeaven
  • 2015年03月16日 17:49
  • 1131
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:字符驱动学习二(阻塞I/O)
举报原因:
原因补充:

(最多只允许输入30个字)