深入浅出Linux设备驱动之阻塞与非阻塞

 

深入浅出Linux设备驱动之阻塞与非阻塞

刺猬@http://blog.csdn.net/littlehedgehog





注: 该系列文章转载自arm+linux chinaunix博客圈圈主之博客——http://blog.chinaunix.net/u/22630 /article_54997.html   为了适合我的编译环境,源代码有改动,但是相信我更改后的代码更加适合现在大多数读者的pc环境。


阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。被挂起的进程进入sleep状态,被从调度器的运行队列移走,直到等待的条件被满足。非阻塞操作的进程在不能进行设备操作时,并不挂起。比如socket里面的read,如果客户端不发送数据,那么服务端就要一直等下去。

在Linux驱动程序中,我们可以使用等待队列(wait queue)来实现阻塞操作。wait queue很早就作为一个基本的功能单位出现在Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。等待队列可以用来同步对系统资源的访问,上节中所讲述Linux信号量在内核中也是由等待队列来实现的。

wait的API:

wait_event(queue, condition)

wait_event_interruptible(queue, condition)

wait_event_timeout(queue, condition, timeout)

wait_event_interruptible_timeout(queue, condition, timeout)

等待condition变为true,否则一直睡眠

condition可以在任何地方被改变,但改变时须wake_up 等待队列wq里面的process。

唤醒函数:

void wake_up(wait_queue_head_t *queue);   //唤醒所有

void wake_up_interruptible(wait_queue_head_t *queue); //唤醒interruptible

下面我们重新定义设备"globalvar",它可以被多个进程打开,但是每次只有当一个进程写入了一个数据之后本进程或其它进程才可以读取该数据,否则一直阻塞。

  1. #ifndef __KERNEL__
  2. #define __KERNEL__
  3. #endif
  4. #ifndef MODULE
  5. #define MODULE
  6. #endif
  7. #include <linux/module.h>
  8. #include <linux/init.h>
  9. #include <linux/fs.h>
  10. #include <linux/wait.h>
  11. #include <linux/sched.h>
  12. #include <asm/uaccess.h>
  13. #include <asm/semaphore.h>
  14. MODULE_LICENSE("GPL");
  15. #define MAJOR_NUM 250 //主设备号
  16. static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
  17. static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
  18. static int globalvar_open(struct inode *,struct file *);
  19. static int globalvar_release(struct inode *,struct file *);
  20. //初始化字符设备驱动的file_operations结构体
  21. struct file_operations globalvar_fops =
  22. {
  23.     read:globalvar_read,
  24.     write:globalvar_write,
  25.     open:globalvar_open,
  26.     release:globalvar_release,
  27. };
  28. static int global_var = 0; //"globalvar"设备的全局变量
  29. static int global_count=0;
  30. static wait_queue_head_t wait_q;    //定义等待队列头部
  31. static int flag=0;      //这个是读写标志
  32. static struct semaphore sem;
  33. static spinlock_t spin=SPIN_LOCK_UNLOCKED;
  34. static int __init globalvar_init(void)
  35. {
  36.     int ret;
  37.     //注册设备驱动
  38.     ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
  39.     if (ret)
  40.         printk("globalvar register failure!/n");
  41.     else
  42.     {
  43.         printk("globalvar register success!/n");
  44.         init_MUTEX(&sem);
  45.         init_waitqueue_head(&wait_q);
  46.     }
  47.     return ret;
  48. }
  49. static void __exit globalvar_exit(void)
  50. {
  51.     printk("globalvar unregister!/n");
  52.     unregister_chrdev(MAJOR_NUM, "globalvar");
  53. }
  54. static int globalvar_open(struct inode *inode,struct file *filep)
  55. {
  56.     spin_lock(&spin);   //这里就限制了只能一个进程
  57.     if(global_count)
  58.     {
  59.         spin_unlock(&spin);
  60.         return -EBUSY;
  61.     }
  62.     global_count++;
  63.     spin_unlock(&spin);
  64.     return 0;
  65. }
  66. static int globalvar_release(struct inode *inode,struct file *filep)
  67. {
  68.     global_count--;
  69.     return 0;
  70. }
  71. static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
  72. {
  73.     printk("0/n");   //这是我作调试用的 
  74.     if(wait_event_interruptible(wait_q,flag!=0))//注意 这里等待的条件flag==0 只要flag!=0 执行write进程会马上唤醒这个进程
  75.     {
  76.         return -ERESTARTSYS;
  77.     }
  78.     
  79.     if(down_interruptible(&sem))
  80.         return -ERESTARTSYS;
  81.     printk("1/n");
  82.     if (copy_to_user(buf, &global_var, sizeof(int)))
  83.     {
  84.         up(&sem);
  85.         return -EFAULT;
  86.     }
  87.     printk("2/n");
  88.     flag=0;
  89.     up(&sem);
  90.     printk("3/n");
  91.     return sizeof(int);
  92. }
  93. static ssize_t globalvar_write(struct file *filp,const char *buf,size_t len,loff_t *off)
  94. {
  95.     if(down_interruptible(&sem))
  96.         return -ERESTARTSYS;
  97. //将用户空间的数据复制到内核空间的global_var
  98.     if (copy_from_user(&global_var, buf, sizeof(int)))
  99.     {
  100.         up(&sem);
  101.         return -EFAULT;
  102.     }
  103.     up(&sem);
  104.     flag=1;
  105.     wake_up_interruptible(&wait_q);
  106.     return sizeof(int);
  107. }
  108. module_init(globalvar_init);
  109. module_exit(globalvar_exit);
  110. MODULE_LICENSE("GPL");
  111. MODULE_AUTHOR("neo");

下面是测试程序,同样,我自己改了下。最好自己写,这样加深了解   [刺猬]

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. //#define DEBUG 
  7. int main()
  8. {
  9.     int fd,readnum=13,writenum=22;
  10.     int pid=1;
  11.     if((fd=open("/dev/test",O_RDWR, S_IRUSR | S_IWUSR))==-1)
  12.     {
  13.         perror("damn! failed");
  14.         return -1;
  15.     }
  16. #ifndef DEBUG
  17.     if((pid=fork())==-1)
  18.         return -1;
  19. #endif
  20.     sleep(3);
  21.     if(pid)
  22.     {
  23.         printf("I am a parent! /nMy work is to read the globalvar device/n");
  24.         sleep(3);
  25.         printf("now read ... until one of my children write into it/n");
  26.         read(fd,&readnum,sizeof(int));
  27.         printf("at last ! someone's written %d my job's done/n",readnum);
  28.     }
  29.     else
  30.     {
  31.         printf("I am a child/nMy work is to write/n");
  32.         sleep(2);
  33.         printf("now write!/n");
  34.         write(fd,&writenum,sizeof(int));
  35.         printf("done! I write %d",writenum);
  36.         sleep(8);
  37.     }
  38.     return 0;
  39. }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值