简介
首先需要明白什么是阻塞,然后明白open函数的的打开标志,进一步明白这些标志是在哪里判断的,是由谁来管理的。
除了读写权限以外,还存在一个O_NONBLOCK 标志,这个标志表征阻塞和非阻塞。
代码
非阻塞
/*-------------------应用层--------------------------*/# 应用层以O_NONBLOCK 标志打开,fd = open(filename, O_RDWR | O_NONBLOCK);/*-------------------驱动层--------------------------*/# 判断是否含有 O_NONBLOCK 标志demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)# 非阻塞模式if (file->f_flags & O_NONBLOCK)# 比如说需要读取的数据还没有准备好,是直接返回,还是睡眠等会再读do somethingreturn -EAGAIN; /*非阻塞模式*/
阻塞模式
非阻塞模式用的比较少,很多时候使用的是组赛模式,如果读取不到数据,那么就睡眠,等待条件满足。
进程睡眠一般含有以下五种,
# include/linux/sched.h#define TASK_RUNNING 0 (就绪态)#define TASK_INTERRUPTIBLE 1 (可中断睡眠态)#define TASK_UNINTERRUPTIBLE 2 (不可中断睡眠态)#define __TASK_STOPPED 4 (中止态)#define __TASK_TRACED 8 (僵尸态)
等待队列
当把一个进程转入睡眠态,就是由TASK_RUNNING转换为TASK_INTERRUPTIBLE态或者TASK_UNINTERRUPTIBLE态。然后这个进程会被调理运行队列(进程调度器操作)。当请求的资源或者事件到来,进程将会被重新唤醒。
include/linux/wait.h:/*---------------------------数据结构体-----------------------------*/struct __wait_queue_head {spinlock_t lock;struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;# 使用 wait_queue_t 来表示等待的队列元素struct __wait_queue {unsigned int flags;void *private;wait_queue_func_t func;struct list_head task_list;};typedef struct __wait_queue wait_queue_t;/*----------------------------API-----------------------------*/DECLARE_WAIT_QUEUE_HEAD(name)/*声明一个等待队列 - 静态*/wait_queue_head_t read_wait;init_waitqueue_head(name)/*声明一个等待队列 - 动态*/
内核睡眠有关函数
include/linux/wait.h#define wait_event(wq, condition)#define wait_event_interruptible(wq, condition)#define wait_event_interruptible_timeout(wq, condition, timeout)#define wait_event_timeout(wq, condition, timeout)# wq 等待队列# condition 布尔表达式# timeout# 1*HZ n*HZ/*------------唤醒---------------*/#define wake_up(x)# 唤醒所有等待队列之中的进程# 配合 wait_event 以及 wait_event_timeout 使用#define wake_up_interruptible(x)# 配合wait_event_interruptible 以及 wait_event_interruptible_timeout 使用
阻塞模式的代码实现
/*首先定义好设备的结构体*/struct mydemo_device {const char *name;struct device *dev;struct miscdevice *miscdev;wait_queue_head_t read_queue; /*读队列*/wait_queue_head_t write_queue; /*写队列*/};/*加一层嵌套*/struct mydemo_private_data {struct mydemo_device *device;};/*-----------静态全局数据-----------------*/static struct mydemo_device *mydemo_device;/*------------init函数--------------------*/simple_char_initstruct mydemo_device *device = kmalloc(sizeof(struct mydemo_device), GFP_KERNEL);/*注册杂项设备*/misc_register(&mydemodrv_misc_device);device->dev = mydemodrv_misc_device.this_device;device->miscdev = &mydemodrv_misc_device;/*比较重要的是这两步,初始化这两个队列*/init_waitqueue_head(&device->read_queue);init_waitqueue_head(&device->write_queue);/*赋值给全局静态函数*/mydemo_device = device;/*------------open函数--------------------*/demodrv_openstruct mydemo_private_data *data;struct mydemo_device *device = mydemo_device;/*私有数据的指针*/data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);data->device = device;/*将私有数据放入文件结构的私有指针里面*/file->private_data = data;/*------------read函数--------------------*/demodrv_read/*获取文件的私有数据*/struct mydemo_private_data *data = file->private_data;/*得到设备指针*/struct mydemo_device *device = data->device;/*假如现在没有数据,等待有数据,在此阻塞*/wait_event_interruptible(device->read_queue,!kfifo_is_empty(&mydemo_fifo)/*现在可以读取数据了*/kfifo_to_user(&mydemo_fifo, buf, count, &actual_readed);
参考资料
进程资源和进程状态 TASK_RUNNING TASK_INTERRUPTIBLE TASK_UNINTERRUPTIBLE
