Yuhang Zheng

第二十五节、等待队列

N 人看过

本节用于介绍等待队列

一、阻塞与非阻塞的概念

阻塞:当前设备如果不可读或不可写时,也就是不能获得资源的时候,那么当前进程程会被挂起。只有当设备满足条件的时候才可以返回。默认情况下,文件都是以这种方式打开。

非阻塞:当前设备不可读或不可写时,该函数不会阻塞当前进程,要么放弃,要么不停的查询,或者直到可以操作为止。

读写函数是否阻塞可以通过参数来指定:

fd=open(filepath,O_RDWR);//默认阻塞打开
fd=open(filepath,O_RDWR|O_NONBLOCK);//非阻塞方式打开

二、等待队列基础知识

当我们进程去访问设备的时候,经常需要等待有特定事件发生以后在继续往下运行,这个时候就需要在驱动里面实现当条件不满足的时候进程休眠,当条件满足的时候在由内核唤醒进程。那么等待队列就实现了在事件上的条件等待。

<1>等待队列头

等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。

等待队列头使用结构体 wait_queue_head_t来表示,

文件位置路径:include/linux/wait.h

struct wait_queue_head {
        spinlock_t              lock;    //自旋锁
        struct list_head        head;    //链表头
};
typedef struct wait_queue_head wait_queue_head_t;

类型名是wait_queue_head_t,只需要记住这个即可。

定义一个等待队列头:

wait_queue_head_t test_wq;//定义一个等待队列的头  

定义等待队列头以后需要初始化,可以使用init_waitqueue_head函数初始化等待队列头,函数原型如下:

文件位置路径:include/linux/wait.h

extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);

#define init_waitqueue_head(q)                          \
        do {                                            \
                static struct lock_class_key __key;     \
                                                        \
                __init_waitqueue_head((q), #q, &__key); \
        } while (0)

也可以使用宏 DECLARE_WAIT_QUEUE_HEAD来一次性完成等待队列头的定义和初始化。

#define DECLARE_WAIT_QUEUE_HEAD(name) \
        wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

三、等待队列相关函数

<1>init_waitqueue_head宏

作用:动态初始化等待队列头结构

函数原型:

文件位置路径:include/linux/wait.h

extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);

#define init_waitqueue_head(q)                          \
        do {                                            \
                static struct lock_class_key __key;     \
                                                        \
                __init_waitqueue_head((q), #q, &__key); \
        } while (0)
//参数:
//q:wait_queue_head_t类型的指针

<2>wait_event宏

作用:不可中断的阻塞等待,让调用进程进入不可中断的睡眠状态,在等待队列里面睡眠直到condition变成真,被内核唤醒。

函数原型:

文件位置路径:include/linux/wait.h

#define wait_event(wq, condition)                                       \
do {                                                                    \
        might_sleep();                                                  \
        if (condition)                                                  \
                break;                                                  \
        __wait_event(wq, condition);                                    \
} while (0)
//参数:
//wq:wait_queue_head_t类型的变量
//condition:等待的条件,为假时才可以进入休眠

注意:调用的时要确认condition值是真还是假,如果调用condition为真,则不会休眠。

<3> wait_event_interruptible宏

作用:可中断的阻塞等待,让调用进程进入可中断的睡眠状态,直到condition变成真被内核唤醒或被信号打断唤醒。

函数原型:

文件位置路径:include/linux/wait.h

#define wait_event_interruptible(wq, condition)                         \
({                                                                      \
        int __ret = 0;                                                  \
        might_sleep();                                                  \
        if (!(condition))                                               \
                __ret = __wait_event_interruptible(wq, condition);      \
        __ret;                                                          \
})
//参数:
//wq:wait_queue_head_t类型的变量
//condition:等待的条件,为假时才可以进入休眠

返回:判断condition是否为真,如果为真则返回,否则检查如果进程是被信号唤醒,会返回-ERESTARTSYS 错误码,如果是condition为真,则返回0

<4>wake_up宏

作用:唤醒所有休眠进程

函数原型:

文件位置路径:include/linux/wait.h

#define wake_up(x)                      __wake_up(x, TASK_NORMAL, 1, NULL)
//参数:
//x:wait_queue_head_t类型的指针

<5>wake_up_interruptible宏

功能:唤醒可中断的休眠进程

函数原型:

文件位置路径:include/linux/wait.h

#define wake_up_interruptible(x)        __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
//参数:
//x:wait_queue_head_t类型的指针