Linux等待队列
2016-09-06 09:19
211 查看
多路IO复用select采用等待队列机制让用户程序没有资源读/写时睡眠,有资源读/写时唤醒用户程序。
等待队列以双向循环链表为基础数据结构,与进程调度紧密结合,用于实现内核的异步事件通知机制,也可用于同步对系统资源的访问。
1. 数据结构
(1) 每个等待队列都有一个等待队列头,该结构定义如下:
每次对等待队列的操作都需要加自旋锁,防止其他进程对等待队列的写操作,实现对等待队列的互斥访问,并且保证临界区的原子性。使用task_list连接一个等待队列项。
(2) 等待队列项,定义如下:
#define WQ_FLAG_EXCLUSIVE 0x01 互斥进程标志,private指向一个task_struct实例,每一个进程使用结构体task_struct来表示。Func是一个函数指针,声明如下:
该函数指针在唤醒进程函数__wake_up_common调用,func默认调用default_wake_function唤醒进程。使用task_list与__wait_queue_head连接。
2. 初始化
(1) 等待队列头初始化。
等待队列头初始化时未加锁,双向链表的prev,next指针都指向wait_queue_head的task_list,也就是双向链表的头节点。
(2) 等待队列项的初始化
3. 添加/删除等待队列项
(1) 添加(头插)
为了保证进程对等待队列的互斥访问,需要加自旋锁,使进程进入临界区
等待进程设置为互斥进程
(2) 删除
4. 等待事件
在等待队列中,进程睡眠直到condition为真。在等待期间,调用___wait_event(wq, condition, state, exclusive, ret, cmd)。该宏主要完成:
a. 创建等待队列成员
b. 在for(;;)中,prepare_to_wait_event使进程在等待队列上等待,并将进程状态置为不可中断TASK_UNINTERRUPTIBLE。
c. 当进程被唤醒时,检查condition是否满足条件,不满足,调用schedule进行进程调度,进程继续睡眠。相反,调用finis_wait将进程状态设置为TASK_RUNNING,并从等待队列删除该进程。
与wait_event类似的函数
wait_event_interruptible(queue,condition);//可被信号打断
wait_event_hrtimeout(queue,condition,timeout);//阻塞等待的超时时间,时间到了,不论condition是否满足,都要返回
wait_event_interruptible_timeout(queue,condition,timeout)
5.唤醒事件
根据进程状态mode,扫描等待队列,调用func(默认为default_wake_function)唤醒进程,直至没有更多的进程被唤醒,或者被唤醒的的独占进程数目已经达到规定数目。
参考资料
http://www.cnblogs.com/gdk-0078/p/5172941.html
http://blog.chinaunix.net/uid-27714502-id-3450323.html
等待队列以双向循环链表为基础数据结构,与进程调度紧密结合,用于实现内核的异步事件通知机制,也可用于同步对系统资源的访问。
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_LOCK_UNLOCKED(name.lock), \ .task_list = { &(name).task_list, &(name).task_list } }
等待队列头初始化时未加锁,双向链表的prev,next指针都指向wait_queue_head的task_list,也就是双向链表的头节点。
(2) 等待队列项的初始化
// 等待队列项初始化 static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) { q->flags = 0; q->private = p; q->func = default_wake_function; } #define DEFINE_WAIT_FUNC(name, function) \ wait_queue_t name = { \ .private = current, \ .func = function, \ .task_list = LIST_HEAD_INIT((name).task_list), \ } #define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function) // autoremove_wake_function 唤醒进程,调用default_wake_function,将被唤醒进程从等待队列删除 // 双向链表prev,next指向空,唤醒函数为default_wake_function,并指定等待的进程。 #define __WAITQUEUE_INITIALIZER(name, tsk) { \ .private = tsk, \ .func = default_wake_function, \ .task_list = { NULL, NULL } } #define DECLARE_WAITQUEUE(name, tsk) \ wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
3. 添加/删除等待队列项
(1) 添加(头插)
为了保证进程对等待队列的互斥访问,需要加自旋锁,使进程进入临界区
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait->flags &= ~WQ_FLAG_EXCLUSIVE; // 非互斥进程 spin_lock_irqsave(&q->lock, flags); // 加锁,等待队列互斥访问,临界区的原子性 __add_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); // 释放锁 } // 采用头插的方法list_add,将new节点插入到head之后 static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) { list_add(&new->task_list, &head->task_list); }
等待进程设置为互斥进程
static inline void __add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) { wait->flags |= WQ_FLAG_EXCLUSIVE; __add_wait_queue(q, wait); }
(2) 删除
//进程唤醒,从wait_queue删除该节点 void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __remove_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); } static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old) { list_del(&old->task_list); }
4. 等待事件
<span style="font-size:10px;">#define wait_event(wq, condition) \ do { \ might_sleep(); \ if (condition) \ break; \ __wait_event(wq, condition); \ } while (0) #define __wait_event(wq, condition) \ (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ schedule()) #define ___wait_event(wq, condition, state, exclusive, ret, cmd) \ ({ \ __label__ __out; \ wait_queue_t __wait; \ long __ret = ret; /* explicit shadow */ \ \ INIT_LIST_HEAD(&__wait.task_list); \ if (exclusive) \ __wait.flags = WQ_FLAG_EXCLUSIVE; \ else \ __wait.flags = 0; \ \ for (;;) { \ long __int = prepare_to_wait_event(&wq, &__wait, state); \ \ if (condition) \ break; \ \ if (___wait_is_interruptible(state) && __int) { \ __ret = __int; \ if (exclusive) { \ abort_exclusive_wait(&wq, &__wait, \ state, NULL); \ goto __out; \ } \ break; \ } \ \ cmd; \ } \ finish_wait(&wq, &__wait); \ __out: __ret; \ })</span>
在等待队列中,进程睡眠直到condition为真。在等待期间,调用___wait_event(wq, condition, state, exclusive, ret, cmd)。该宏主要完成:
a. 创建等待队列成员
b. 在for(;;)中,prepare_to_wait_event使进程在等待队列上等待,并将进程状态置为不可中断TASK_UNINTERRUPTIBLE。
c. 当进程被唤醒时,检查condition是否满足条件,不满足,调用schedule进行进程调度,进程继续睡眠。相反,调用finis_wait将进程状态设置为TASK_RUNNING,并从等待队列删除该进程。
与wait_event类似的函数
wait_event_interruptible(queue,condition);//可被信号打断
wait_event_hrtimeout(queue,condition,timeout);//阻塞等待的超时时间,时间到了,不论condition是否满足,都要返回
wait_event_interruptible_timeout(queue,condition,timeout)
5.唤醒事件
//唤醒等待队列.可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE状态的进程,和wait_event/wait_event_timeout成对使用 void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __wake_up_common(q, mode, nr_exclusive, 0, key); spin_unlock_irqrestore(&q->lock, flags); } #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) //和wake_up()唯一的区别是它只能唤醒TASK_INTERRUPTIBLE状态的进程.,与wait_event_interruptible, wait_event_interruptible_timeout,wait_event_interruptible_exclusive成对使用 #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL) #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL) #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL) //这些也基本都和wake_up/wake_up_interruptible一样 __wake_up 在临界区调用__wake_up_common() static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { wait_queue_t *curr, *next; list_for_each_entry_safe(curr, next, &q->task_list, task_list) { unsigned flags = curr->flags; if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } }
根据进程状态mode,扫描等待队列,调用func(默认为default_wake_function)唤醒进程,直至没有更多的进程被唤醒,或者被唤醒的的独占进程数目已经达到规定数目。
参考资料
http://www.cnblogs.com/gdk-0078/p/5172941.html
http://blog.chinaunix.net/uid-27714502-id-3450323.html
相关文章推荐
- linux内核的 等待队列 使用方法,wait_queue_head_t,进程休眠
- linux等待队列(转载)
- linux poll 和 等待队列休眠的关系
- Linux驱动阻塞与非阻塞IO之等待队列
- Linux等待队列简介
- Linux中等待队列机制分析
- linux进程的休眠(等待队列)
- linux等待队列
- linux内核中等待队列 (函数wait_event与wake_up)
- linux等待队列(转载)
- Linux内核等待队列机制介绍(转自http://plinux.org)
- linux进程的休眠(等待队列)
- linux等待队列使用步骤
- linux 等待队列 PK linux 等待队列头
- Linux内核的等待队列
- Linux中等待队列机制分析
- linux等待队列使用步骤
- linux内核的 等待队列 使用方法,wait_queue_head_t,进程休眠
- Linux内核等待队列机制介绍
- linux高级字符设备驱动之 二 内核等待队列