等待队列和异步信号
来源:互联网 发布:绝地大师 软件 编辑:程序博客网 时间:2024/06/07 13:01
(name) -- 生成一个等待队列头wait_queue_head_t,名字为name
-----------------------------------------------------------------
#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 } }
typedef struct __wait_queue_head wait_queue_head_t ;
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
在wait_event_interruptible()中首先判断condition是不是已经满足,如果是则直接返回0,否则调用__wait_event_interruptible(),并用__ret来存放返回值
---------------------------------------------------------------
#define wait_event_interruptible(wq, condition) /
({ /
int __ret = 0; /
if (!(condition)) /
__wait_event_interruptible(wq, condition, __ret);/
__ret; /
})
wait queue很早就作为一个基本的功能单位出现在Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。我们从它的使用范例着手,看看等待队列是如何实现异步信号功能的。
在核心运行过程中,经常会因为某些条件不满足而需要挂起当前线程,直至条件满足了才继续执行。在2.4内核中提供了一组新接口来实现这样的功能,下面的代码节选自kernel/printk.c:
unsigned long log_size;1: DECLARE_WAIT_QUEUE_HEAD(log_wait);...4: spinlock_t console_lock = SPIN_LOCK_UNLOCKED;... int do_syslog(int type,char *buf,int len){ ...2: error=wait_event_interruptible(log_wait,log_size); if(error) goto out; ...5: spin_lock_irq(&console_lock);... log_size--; ...6:spin_unlock_irq(&console_lock); ... } asmlinkage int printk(const char *fmt,...){...7:spin_lock_irqsave(&console_lock,flags); ... log_size++;...8:spin_unlock_irqrestore(&console_lock,flags);3: wake_up_interruptible(&log_wait); ... }
这段代码实现了printk调用和syslog之间的同步,syslog需要等待printk送数据到缓冲区,因此,在2:处等待log_size非0;而printk一边传送数据,一边增加log_size的值,完成后唤醒在log_wait上等待的所有线程(这个线程不是用户空间的线程概念,而是核内的一个执行序列)。执行了3:的wake_up_interruptible()后,2:处的wait_event_interruptible()返回0,从而进入syslog的实际动作。
1:是定义log_wait全局变量的宏调用。
在实际操作log_size全局变量的时候,还使用了spin_lock自旋锁来实现互斥,关于自旋锁,这里暂不作解释,但从这段代码中已经可以清楚的知道它的使用方法了。
所有wait queue使用上的技巧体现在wait_event_interruptible()的实现上,代码位于include/linux/sched.h中,前置数字表示行号:
779 #define __wait_event_interruptible(wq, condition, ret) /780 do { /781 wait_queue_t __wait; /782 init_waitqueue_entry(&__wait, current); /783 /784 add_wait_queue(&wq, &__wait); /785 for (;;) { /786 set_current_state(TASK_INTERRUPTIBLE); /787 if (condition) /788 break; /789 if (!signal_pending(current)) { /790 schedule(); /791 continue; /792 } /793 ret = -ERESTARTSYS; /794 break; /795 } /796 current->state = TASK_RUNNING; /797 remove_wait_queue(&wq, &__wait); /798 } while (0)799 800 #define wait_event_interruptible(wq, condition) /801 ({ /802 int __ret = 0; /803 if (!(condition)) /804 __wait_event_interruptible(wq, condition, __ret); /805 __ret; /806 })
在wait_event_interruptible()中首先判断condition是不是已经满足,如果是则直接返回0,否则调用__wait_event_interruptible(),并用__ret来存放返回值。__wait_event_interruptible()首先定义并初始化一个wait_queue_t变量__wait,其中数据为当前进程结构current(struct task_struct),并把__wait入队。在无限循环中,__wait_event_interruptible()将本进程置为可中断的挂起状态,反复检查condition是否成立,如果成立则退出,如果不成立则继续休眠;条件满足后,即把本进程运行状态置为运行态,并将__wait从等待队列中清除掉,从而进程能够调度运行。如果进程当前有异步信号(POSIX的),则返回-ERESTARTSYS。
挂起的进程并不会自动转入运行的,因此,还需要一个唤醒动作,这个动作由wake_up_interruptible()完成,它将遍历作为参数传入的log_wait等待队列,将其中所有的元素(通常都是task_struct)置为运行态,从而可被调度到,执行__wait_event_interruptible()中的代码。
DECLARE_WAIT_QUEUE_HEAD(log_wait)经过宏展开后就是定义了一个log_wait等待队列头变量:
struct __wait_queue_head log_wait = {lock:SPIN_LOCK_UNLOCKED,task_list: { &log_wait.task_list, &log_wait.task_list }}
其中task_list是struct list_head变量,包括两个list_head指针,一个next、一个prev,这里把它们初始化为自身,属于队列实现上的技巧,其细节可以参阅关于内核list数据结构的讨论,add_wait_queue()和remove_wait_queue()就等同于list_add()和list_del()。
wait_queue_t结构在include/linux/wait.h中定义,关键元素即为一个struct task_struct变量表征当前进程。
除了wait_event_interruptible()/wake_up_interruptible()以外,与此相对应的还有wait_event()和wake_up()接口,interruptible是更安全、更常用的选择,因为可中断的等待可以接收信号,从而挂起的进程允许被外界kill。
wait_event*()接口是2.4内核引入并推荐使用的,在此之前,最常用的等待操作是interruptible_sleep_on(wait_queue_head_t *wq),当然,与此配套的还有不可中断版本sleep_on(),另外,还有带有超时控制的*sleep_on_timeout()。sleep_on系列函数的语义比wait_event简单,没有条件判断功能,其余动作与wait_event完全相同,也就是说,我们可以用interruptible_sleep_on()来实现wait_event_interruptible()(仅作示意〉:
do{interruptible_sleep_on(&log_wait); if(condition)break;}while(1);
相对而言,这种操作序列有反复的入队、出队动作,更加耗时,而很大一部分等待操作的确是需要判断一个条件是否满足的,因此2.4才推荐使用wait_event接口。
在wake_up系列接口中,还有一类wake_up_sync()和wake_up_interruptible_sync()接口,保证调度在wake_up返回之后进行。
- 等待队列和异步信号
- SIGCHLD信号与进程异步等待
- 等待队列和工作队列
- 等待队列和工作队列
- 信号量,等待队列,异步通知,驱动例子
- 等待队列和完成量
- Socket层实现系列 — 信号驱动的异步等待
- 关于子进程异步等待方式(SIGCHLD信号)
- Linux子进程的异步等待方式(SIGCHLD信号)
- 子进程的异步等待方式——SIGCHLD信号
- 有关 等待队列和wait_event_interruptible() 和 wake_up()
- 异步等待
- linux等待队列wait_queue_head_t和wait_queue_t
- linux等待队列wait_queue_head_t和wait_queue_t
- linux等待队列wait_queue_head_t和wait_queue_t
- linux等待队列wait_queue_head_t和wait_queue_t
- linux等待队列wait_queue_head_t和wait_queue_t
- linux等待队列wait_queue_head_t和wait_queue_t
- Visual Studio 2010
- ubuntu glassfishv3
- 博客收藏夹
- 在Ext中如何使窗体总在最前面(how to set a window always on top in Ext)
- 组合数的素数算法(ACM基础教程1010)
- 等待队列和异步信号
- 自制6410核心板终于调试成功了
- 多重继承下, 执行期 this 指针的调整示例代码
- db2 体系结构
- director打开各种格式文档
- Android getWidth和getMeasuredWidth的正解
- php 中的 __FILE__ 和 dirname(__FILE__)
- 系统初始化脚本(根据具体需求关闭不需要的服务)
- Collections 操作集合的工具类