简介
首先需要明白什么是阻塞,然后明白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 something
return -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_init
struct 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_open
struct 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