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

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

  1. static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  2.     62    {
  3.     63        int ret = 0;
  4.     64        struct cfifo_t *dev = filp->private_data;
  5.     65        DECLARE_WAITQUEUE(wait, current);
  6.     66    
  7.     67        down(&dev->sem);
  8.     68        add_wait_queue(&dev->r_wait, &wait);
  9.     69    
  10.     70        while(dev->current_len == 0){
  11.     71            if(filp->f_flags & O_NONBLOCK){
  12.     72                ret = -EAGAIN;
  13.     73                goto out;
  14.     74            }
  15.     75    
  16.     76            __set_current_state(TASK_INTERRUPTIBLE);
  17.     77            up(&dev->sem);
  18.     78    
  19.     79            schedule(); /*调度其他进程运行*/
  20.     80            if(signal_pending(current)){
  21.     81                ret = -ERESTARTSYS;
  22.     82                goto out2;
  23.     83            }
  24.     84    
  25.     85            down(&dev->sem);
  26.     86        }
  27.     87    
  28.     88        if(count > dev->current_len)
  29.     89            count = dev->current_len;
  30.     90    
  31.     91        if(copy_to_user(buf, (void*)(dev->mem), count)){
  32.     92            ret = -EFAULT;
  33.     93            goto out;
  34.     94        } else{
  35.     95            memcpy(dev->mem, dev->mem + count, dev->current_len - count);
  36.     96            dev->current_len -= count;
  37.     97            *fpos =dev->current_len;
  38.     98    
  39.     99            wake_up_interruptible(&dev->w_wait);
  40.    100            ret =count;
  41.    101    
  42.    102            printk(KERN_INFO "read %s %d bites \n", buf, count);
  43.    103        }
  44.    104    
  45.    105    out:
  46.    106        up(&dev->sem);
  47.    107    out2:
  48.    108        remove_wait_queue(&dev->r_wait, &wait);
  49.    109        set_current_state(TASK_RUNNING);
  50.    110    
  51.    111        return ret;
  52.    112    }
  53.    113
  1. static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  2.    115    {
  3.    116        int ret = 0;
  4.    117        struct cfifo_t *dev = filp->private_data;
  5.    118        DECLARE_WAITQUEUE(wait, current);
  6.    119    
  7.    120        down(&dev->sem);
  8.    121        add_wait_queue(&dev->w_wait, &wait);
  9.    122    
  10.    123        while(dev->current_len >= GLOBALMEM_SIZE){
  11.    124            if(filp->f_flags & O_NONBLOCK){             
  12.    125                ret = -EAGAIN;
  13.    126                goto out;
  14.    127            }
  15.    128    
  16.    129            __set_current_state(TASK_INTERRUPTIBLE);    /* 进程状态,定义在 linux/sched.h */
  17.    130            up(&dev->sem);                              /* 释放信号量,避免死锁 */
  18.    131    
  19.    132            schedule();                                 /* 调度其他进程运行*/
  20.    133            if(signal_pending(current)){                
  21.    134                ret = -ERESTARTSYS;
  22.    135                goto out2;
  23.    136            }
  24.    137    
  25.    138            down(&dev->sem);
  26.    139        }
  27.    140    
  28.    141        if(count > GLOBALMEM_SIZE - dev->current_len)
  29.    142            count = GLOBALMEM_SIZE - dev->current_len;
  30.    143    
  31.    144    
  32.    145        if(copy_from_user(dev->mem+dev->current_len, buf, count)){
  33.    146            ret = - EFAULT;
  34.    147            goto out;
  35.    148        }
  36.    149        else{
  37.    150            dev->current_len += count;
  38.    151            *fpos = dev->current_len;
  39.    152            ret = count;
  40.    153    
  41.    154            wake_up_interruptible(&dev->r_wait);
  42.    155            printk(KERN_INFO "write %s %d bites \n", buf, count);
  43.    156        }
  44.    157    
  45.    158    out:
  46.    159        up(&dev->sem);
  47.    160    out2:
  48.    161        remove_wait_queue(&dev->w_wait, &wait);
  49.    162        set_current_state(TASK_RUNNING);
  50.    163    
  51.    164        return ret;
  52.    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:

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

相关文章推荐

ldd3学习之十二(2):高级字符驱动程序操作--等待队列,阻塞I/O,休眠

在应用程序调用read,write时,若驱动程序无法立即满足要求,该如何响应?驱动程序应该(默认)阻塞该进程,将其置入休眠状态直到请求可继续。1.休眠进程被置为休眠,意味着它被标识为处于一个特殊的状态...

Linux设备驱动程序学习(5) -高级字符驱动程序操作[(2)阻塞型I/O和休眠]

这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346) 一、休眠 进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移...
  • sadamoo
  • sadamoo
  • 2012年09月07日 17:10
  • 1686

Linux设备驱动程序学习(5)-高级字符驱动程序操作〔(2)阻塞型I/O和休眠〕

Linux设备驱动程序学习(5) -高级字符驱动程序操作〔(2)阻塞型I/O和休眠〕 这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346) 一、休眠 进程被置为...

Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型I/O和休眠]

这一部分主要讨论:假如驱动程式无法立即满足请求,该如何响应?(65865346) 一、休眠 进程被置为休眠,意味着他被标识为处于一个特别的状态并且从调度器的运行队列中移走。这个进程将不被在任何 C...
  • jeffade
  • jeffade
  • 2012年05月11日 14:49
  • 262

高级字符驱动程序操作[(2)阻塞型I/O和休眠]

高级字符驱动程序操作[(2)阻塞型I/O和休眠] 这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346)  一、休眠进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调...

【Linux 驱动】第六章 高级字符驱动程序操作 ----阻塞型I/O

序言:试想如果在驱动方法中的read/write中,当数据不可用时,用户可能调用read,当输出缓冲区满时,设备并未准备好接受数据,这种情况下驱动程序可以阻塞该进程,并且置入休眠状态直到满足条件。 ...

雾山的Java学习笔记---I/O(二)(字符流)

首先讲一下字节流和字符流的却别

linux 驱动学习之阻塞 I/O

为了将进程以一种安全的方式进入休眠,我们需要牢记两条规则: 一、永远不要在原子上下文中进入休眠。 二、进程休眠后,对环境一无所知。唤醒后,必须再次检查以确保我们等待的条件真正为真 测试例子只...

【Linux驱动】阻塞型I/O(二+并发控制)

承接上文,这里继续学习linux内核驱动并发控制阻塞型I/O。 废话不多说,直接看代码,基础接口函数请自行查阅相关资料,比如《LDD》。 另外并发控制信号量和linux应用层的信号量概念和原理是差...

Linux设备驱动程序学习 高级字符驱动程序操作[阻塞型I/O和非阻塞I/O]

阻塞型I/O和非阻塞I/O 阻塞:休眠 非阻塞:异步通知 一 休眠 安全地进入休眠的两条规则: (1)      永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、s...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:字符驱动学习二(阻塞I/O)
举报原因:
原因补充:

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