1.等待队列 wait_queue_head_t
等待队列用于使得进程等待某一特定事件的发生,无需频繁的轮询,进程在等待周期中睡眠,当事件发生后由内核自动唤醒。
等待队列是一种实现阻塞和唤醒的内核机制,很早就作为一个基本的功能单位出现在Linux内核中,它以队列为基础数据结构,
与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。
2.使用过程
A、定义和初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD(wait);
或者动态地, 如下:
wait_queue_head_t wait;
init_waitqueue_head(&wait);
B、休眠进程
wait_event(wait, condition)
wait_event_interruptible(wait, condition)
wait_event_timeout(wait, condition, timeout)
wait_event_interruptible_timeout(wait, condition, timeout)
备注:
wait 是等待队列头, condition 是条件,如果调用 wait_event 前 condition == 0 ,则调用 wait_event 之后,当前
进程就会休眠。
TASK_INTERRUPTIBLE 与 TASK_UNINTERRUPTIBLE 区别在于,它的休眠是否会被信号打断,别的进程发来一个信号比如 kill ,
TASK_INTERRUPTIBLE 就会醒来去处理。然而 TASK_UNINTERRUPTIBLE 不会。schedule(),进程调度,而 schedule_timeout()
进行调度之后,一定时间后自动唤醒。
C、唤醒进程
void wake_up(wait_queue_head_t *wait);
void wake_up_interruptible(wait_queue_head_t *wait);
备注:
对应于不同的进程状态,使用不同的唤醒函数。比如你调用 wake_up 去唤醒一个使用 wait_event 等,进入休眠的进程,唤醒
之后,它会判断 condition 是否为真,如果还是假的继续睡眠。
3.简单例子
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
MODULE_AUTHOR("Jimmy");
MODULE_DESCRIPTION("wait queue example");
MODULE_LICENSE("GPL");
static int condition;
static struct task_struct *task_1;
static struct task_struct *task_2;
static struct task_struct *task_3;
DECLARE_WAIT_QUEUE_HEAD(wq);
static int thread_func_1(void *data)
{
msleep(100);
wait_event_interruptible(wq, condition);
condition = 0;
printk(">>>>>this task 1\n");
do {
msleep(1000);
}while(!kthread_should_stop());
return 1;
}
static int thread_func_2(void *data)
{
wait_event_interruptible(wq, condition);
condition = 0;
printk(">>>>>this task 2\n");
do {
msleep(1000);
}while(!kthread_should_stop());
return 2;
}
static int thread_func_3(void *data)
{
msleep(2000);
printk(">>>>>this task 3\n");
condition = 1;
wake_up_interruptible(&wq);
msleep(2000);
condition = 1;
wake_up_interruptible(&wq);
do {
msleep(1000);
}while(!kthread_should_stop());
return 3;
}
static int __init mod_init(void)
{
condition = 0;
task_1 = kthread_run(thread_func_1, NULL, "thread%d", 1);
if (IS_ERR(task_1)) {
printk("******create thread 1 failed\n");
} else {
printk("======success create thread 1\n");
}
task_2 = kthread_run(thread_func_2, NULL, "thread%d", 2);
if (IS_ERR(task_2)) {
printk("******create thread 2 failed\n");
} else {
printk("======success create thread 2\n");
}
task_3 = kthread_run(thread_func_3, NULL, "thread%d", 3);
if (IS_ERR(task_2)) {
printk("******create thread 3 failed\n");
} else {
printk("======success create thread 3\n");
}
return 0;
}
static void __exit mod_exit(void)
{
int ret;
if (!IS_ERR(task_1)) {
ret = kthread_stop(task_1);
printk("<<<<<<<<task 1 exit, ret = %d\n", ret);
}
if (!IS_ERR(task_2)) {
ret = kthread_stop(task_2);
printk("<<<<<<<<task 2 exit, ret = %d\n", ret);
}
if (!IS_ERR(task_3)) {
ret = kthread_stop(task_3);
printk("<<<<<<<<task 3 exit, ret = %d\n", ret);
}
return;
}
module_init(mod_init);
module_exit(mod_exit);
备注:
注意:
1、要从wait_event()函数跳出,需要两个条件。1、condition = 1; 2、在内核的另一个地方调用了wake_up()函数。
2、wake_up()每次只能唤醒一个进程,而且是从队列头开始唤醒的,而wait_event()函数每次会将新建的等待队列插
到队列头,因此最后调用wait_event()函数的进程先被唤醒!如果要唤醒某个特定的进程,没有现成的函数,按
照本人理解,只能使用wake_up_all()函数唤醒所有进程,然后在通过条件condition来控制(每个进程使用不同的
变量来控制,在wake_up_all()函数后只将要唤醒的进程的变量置成真)。
4.总结
static DECLARE_WAIT_QUEUE_HEAD(bat_thread_wq);
wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE));
bat_thread_timeout = KAL_FALSE;
bat_thread_timeout = KAL_TRUE;
wake_up(&bat_thread_wq);