简介

首先需要明白什么是阻塞,然后明白open函数的的打开标志,进一步明白这些标志是在哪里判断的,是由谁来管理的。
除了读写权限以外,还存在一个O_NONBLOCK 标志,这个标志表征阻塞和非阻塞。

代码

非阻塞

  1. /*-------------------应用层--------------------------*/
  2. # 应用层以O_NONBLOCK 标志打开,
  3. fd = open(filename, O_RDWR | O_NONBLOCK);
  4. /*-------------------驱动层--------------------------*/
  5. # 判断是否含有 O_NONBLOCK 标志
  6. demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  7. # 非阻塞模式
  8. if (file->f_flags & O_NONBLOCK)
  9. # 比如说需要读取的数据还没有准备好,是直接返回,还是睡眠等会再读
  10. do something
  11. return -EAGAIN; /*非阻塞模式*/

阻塞模式

非阻塞模式用的比较少,很多时候使用的是组赛模式,如果读取不到数据,那么就睡眠,等待条件满足。
进程睡眠一般含有以下五种,

  1. # include/linux/sched.h
  2. #define TASK_RUNNING 0 (就绪态)
  3. #define TASK_INTERRUPTIBLE 1 (可中断睡眠态)
  4. #define TASK_UNINTERRUPTIBLE 2 (不可中断睡眠态)
  5. #define __TASK_STOPPED 4 (中止态)
  6. #define __TASK_TRACED 8 (僵尸态)

等待队列

当把一个进程转入睡眠态,就是由TASK_RUNNING转换为TASK_INTERRUPTIBLE态或者TASK_UNINTERRUPTIBLE态。然后这个进程会被调理运行队列(进程调度器操作)。当请求的资源或者事件到来,进程将会被重新唤醒。

  1. include/linux/wait.h:
  2. /*---------------------------数据结构体-----------------------------*/
  3. struct __wait_queue_head {
  4. spinlock_t lock;
  5. struct list_head task_list;
  6. };
  7. typedef struct __wait_queue_head wait_queue_head_t;
  8. # 使用 wait_queue_t 来表示等待的队列元素
  9. struct __wait_queue {
  10. unsigned int flags;
  11. void *private;
  12. wait_queue_func_t func;
  13. struct list_head task_list;
  14. };
  15. typedef struct __wait_queue wait_queue_t;
  16. /*----------------------------API-----------------------------*/
  17. DECLARE_WAIT_QUEUE_HEAD(name)
  18. /*声明一个等待队列 - 静态*/
  19. wait_queue_head_t read_wait;
  20. init_waitqueue_head(name)
  21. /*声明一个等待队列 - 动态*/

内核睡眠有关函数

  1. include/linux/wait.h
  2. #define wait_event(wq, condition)
  3. #define wait_event_interruptible(wq, condition)
  4. #define wait_event_interruptible_timeout(wq, condition, timeout)
  5. #define wait_event_timeout(wq, condition, timeout)
  6. # wq 等待队列
  7. # condition 布尔表达式
  8. # timeout
  9. # 1*HZ n*HZ
  10. /*------------唤醒---------------*/
  11. #define wake_up(x)
  12. # 唤醒所有等待队列之中的进程
  13. # 配合 wait_event 以及 wait_event_timeout 使用
  14. #define wake_up_interruptible(x)
  15. # 配合wait_event_interruptible 以及 wait_event_interruptible_timeout 使用

阻塞模式的代码实现

  1. /*首先定义好设备的结构体*/
  2. struct mydemo_device {
  3. const char *name;
  4. struct device *dev;
  5. struct miscdevice *miscdev;
  6. wait_queue_head_t read_queue; /*读队列*/
  7. wait_queue_head_t write_queue; /*写队列*/
  8. };
  9. /*加一层嵌套*/
  10. struct mydemo_private_data {
  11. struct mydemo_device *device;
  12. };
  13. /*-----------静态全局数据-----------------*/
  14. static struct mydemo_device *mydemo_device;
  15. /*------------init函数--------------------*/
  16. simple_char_init
  17. struct mydemo_device *device = kmalloc(sizeof(struct mydemo_device), GFP_KERNEL);
  18. /*注册杂项设备*/
  19. misc_register(&mydemodrv_misc_device);
  20. device->dev = mydemodrv_misc_device.this_device;
  21. device->miscdev = &mydemodrv_misc_device;
  22. /*比较重要的是这两步,初始化这两个队列*/
  23. init_waitqueue_head(&device->read_queue);
  24. init_waitqueue_head(&device->write_queue);
  25. /*赋值给全局静态函数*/
  26. mydemo_device = device;
  27. /*------------open函数--------------------*/
  28. demodrv_open
  29. struct mydemo_private_data *data;
  30. struct mydemo_device *device = mydemo_device;
  31. /*私有数据的指针*/
  32. data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
  33. data->device = device;
  34. /*将私有数据放入文件结构的私有指针里面*/
  35. file->private_data = data;
  36. /*------------read函数--------------------*/
  37. demodrv_read
  38. /*获取文件的私有数据*/
  39. struct mydemo_private_data *data = file->private_data;
  40. /*得到设备指针*/
  41. struct mydemo_device *device = data->device;
  42. /*假如现在没有数据,等待有数据,在此阻塞*/
  43. wait_event_interruptible(device->read_queue,
  44. !kfifo_is_empty(&mydemo_fifo)
  45. /*现在可以读取数据了*/
  46. kfifo_to_user(&mydemo_fifo, buf, count, &actual_readed);

参考资料

进程资源和进程状态 TASK_RUNNING TASK_INTERRUPTIBLE TASK_UNINTERRUPTIBLE