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

     上次调试好了一个基本的字符驱动,支持并发控制。在实际的使用中,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:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值