linux 内核等待队列

内核调度器管理要运行的任务列表,被称为运行队列。睡眠的进程不再被调度,除非被唤醒。进入睡眠等待的进程,可以释放处理器。

等待队列

等待队列实际上用于处理被阻塞的IO,以等待特定条件成立,并感知数据或资源的可用性。

// include/linux/wait.h

struct wait_queue_entry {
	unsigned int		flags;
	void			*private;
	wait_queue_func_t	func;
	struct list_head	entry;
};

entry这个字段是将进程加入到等待队列的链表中,每个进程都会进入睡眠状态,直到条件变为真。等待队列可以被看作简单的进程链表和锁。

// 静态声明
DECLARE_WAIT_QUEUE_HEAD(name);

// 动态声明
wait_queue_head_t my_wait_queue;
init_waitqueue_head(&my_wait_queue);

// 阻塞
// 如果条件为false,则阻塞等待队列中的当前任务(进程)。
int wait_event_interruptible(wait_queue_head_t q, CONDITION);
/**
 * wait_event_interruptible - sleep until a condition gets true
 * @wq_head: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 *
 * The process is put to sleep (TASK_INTERRUPTIBLE) until the
 * @condition evaluates to true or a signal is received.
 * The @condition is checked each time the waitqueue @wq_head is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 *
 * The function will return -ERESTARTSYS if it was interrupted by a
 * signal and 0 if @condition evaluated to true.
 */
 // 一直睡眠直到条件变为真。
 // 在条件变为true或者收到信号之前,进程一直处于可中断睡眠状态(TASK_INTERRUPTIBLE)。
 // 条件在每次唤醒等待队列时被检查。
 // wake_up 一般在条件在相关变量变化时被被调用。
 // 如果函数被信号中断,则该函数将返回 -ERESTARTSYS,如果@condition计算结果为 true,则返回 0。
#define wait_event_interruptible(wq_head, condition)                            \
({                                                                              \
        int __ret = 0;                                                          \
        might_sleep();                                                          \
        if (!(condition))                                                       \
                __ret = __wait_event_interruptible(wq_head, condition);         \
        __ret;                                                                  \
})


// 解除阻塞
// 如果上述条件为true,则唤醒在等待队列中休眠的进程。
void wake_up_interruptible(wait_queue_head_t *q);
#define wake_up_interruptible(x)        __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
/**
 * __wake_up - wake up threads blocked on a waitqueue.
 * @wq_head: the waitqueue
 * @mode: which threads
 * @nr_exclusive: how many wake-one or wake-many threads to wake up
 * @key: is directly passed to the wakeup function
 *
 * It may be assumed that this function implies a write memory barrier before
 * changing the task state if and only if any tasks are woken up.
 */
void __wake_up(struct wait_queue_head *wq_head, unsigned int mode,
                        int nr_exclusive, void *key)
{
        __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
}
EXPORT_SYMBOL(__wake_up);

wait_event_interruptible 不会持续轮询,而只是在被调用时评估条件。

  • 如果条件为假,则进程将进入TASK_INTERRUPTIBLE 状态并从运行队列中删除,之后每次在等待队列中调用wait_up_interruptible时,都会重新检查条件。
  • 如果wait_up_interruptible运行时发现条件为真,则等待队列中的进程将被唤醒。并将进程状态设置为TASK_RUNNING。进程按照他们进入睡眠的顺序唤醒(先进先出),要唤醒等待队列中等待的所有进程,应该使用wake_up_interruptible_all
#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)

如果调用wake_upwake_up_interruptible,条件为false,则什么都不会发生。如果没有显式调用,进程将永远不会被唤醒。

等待队列的测试程序

// wait_queue.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

static DECLARE_WAIT_QUEUE_HEAD(my_wq);
static int condition = 0;

/* 声明一个工作队列 */
static struct work_struct work;

static void work_handler(struct work_struct *work)
{
        printk("Workqueue module handler!\n");
        msleep(5000);
        printk("Wake up the sleeping module!\n");
        condition = 1;
        wake_up_interruptible(&my_wq);
}

static int __init test_init(void)
{
        printk("Wait queue test module!\n");

        INIT_WORK(&work, work_handler);
        schedule_work(&work);

        printk("Going to sleep %s!\n", __func__);
        wait_event_interruptible(my_wq, condition != 0);

        printk("Woken up by work job!\n");
        return 0;
}

void test_exit(void)
{
        printk("Waitqueue example cleanup!\n");
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
[25596.170555] Wait queue test module!
[25596.170560] Going to sleep test_init!
[25596.170565] Workqueue module handler!
[25601.200917] Wake up the sleeping module!
[25601.200931] Woken up by work job!

内核还提供了一个函数,当condtion或者时延时时间到了,等待队列中的进程恢复运行。

/**
 * wait_event_interruptible_timeout - sleep until a condition gets true or a timeout elapses
 * @wq_head: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 * @timeout: timeout, in jiffies
 *
 * The process is put to sleep (TASK_INTERRUPTIBLE) until the
 * @condition evaluates to true or a signal is received.
 * The @condition is checked each time the waitqueue @wq_head is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 *
 * Returns:
 * 0 if the @condition evaluated to %false after the @timeout elapsed,
 * 1 if the @condition evaluated to %true after the @timeout elapsed,
 * the remaining jiffies (at least 1) if the @condition evaluated
 * to %true before the @timeout elapsed, or -%ERESTARTSYS if it was
 * interrupted by a signal.
 */
#define wait_event_interruptible_timeout(wq_head, condition, timeout)		\
({										\
	long __ret = timeout;							\
	might_sleep();								\
	if (!___wait_cond_timeout(condition))					\
		__ret = __wait_event_interruptible_timeout(wq_head,		\
						condition, timeout);		\
	__ret;									\
})

这里的timeout用的单位是jiffies,所以需要有个函数,将延时时间转换为jiffies

unsigned int jiffies_to_msecs(const unsigned long j);
unsigned int jiffies_to_usecs(const unsigned long j);
unsigned long msecs_to_jiffies(const unsigned int m);
unsigned long usecs_to_jiffies(const unsigned int u);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值