多路IO复用select采用等待队列机制让用户程序没有资源读/写时睡眠,有资源读/写时唤醒用户程序。
等待队列以双向循环链表为基础数据结构,与进程调度紧密结合,用于实现内核的异步事件通知机制,也可用于同步对系统资源的访问。
1. 数据结构
(1) 每个等待队列都有一个等待队列头,该结构定义如下:
struct __wait_queue_head {
spinlock_t lock; //自旋锁,在对task_list操作时,使用该锁实现对等待队列的互斥访问
struct list_head task_list; // 双向循环链表
};
每次对等待队列的操作都需要加自旋锁,防止其他进程对等待队列的写操作,实现对等待队列的互斥访问,并且保证临界区的原子性。使用task_list连接一个等待队列项。
(2) 等待队列项,定义如下:
//等待队列项。每个等待任务都会抽象成为一个_wait_queue,并且挂载在__wait_queue_head
struct __wait_queue {
unsigned int flags; // 互斥/非互斥进程
#define WQ_FLAG_EXCLUSIVE 0x01
void *private; // 指向一个task_struct实例
wait_queue_func_t func; // 函数指针,用于唤醒进程
struct list_head task_list; // 挂入__wait_queue_head
};
typedef struct __wait_queue_head wait_queue_head_t;
typedef struct __wait_queue wait_queue_t;
#define WQ_FLAG_EXCLUSIVE 0x01 互斥进程标志,private指向一个task_struct实例,每一个进程使用结构体task_struct来表示。Func是一个函数指针,声明如下:
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
该函数指针在唤醒进程函数__wake_up_common调用,func默认调用default_wake_function唤醒进程。使用task_list与__wait_queue_head连接。
2. 初始化
(1) 等待队列头初始化。
//初始化
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
// 初始化未加锁
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
.lock = __SPIN