wait_queue机制浅析

wait_queue机制是Linux内核中用于任务调度的重要组件,它允许任务在等待特定事件发生时进入休眠状态,节省CPU资源。当事件触发时,通过wake_up函数能够唤醒等待的任务。wait_event函数会检查条件,若不满足则将任务挂起,而wake_up则唤醒等待链表上的任务。此机制涉及的数据结构包括wait_queue_head(事件链表)和wait_queue_entry(任务等待项)。理解wait_queue涉及到的任务状态转换、链表操作以及中断处理等核心概念。

wait_queue机制浅析

在内核中,如果一个任务需要等待一个事件,如何实现当事件未发生时该任务睡眠节省CPU资源,当事件发生时任务及时被唤醒继续工作呢?wait_event/wake_up机制是一个不错的选择。下面这个场景展示了wait_queue的基本用法

taskAschedule_systemtaskBdo somethingwait_eventschedulesleepthe event happenedwake_upschedulescheduledo somthingtaskAschedule_systemtaskB
void taskA(void) {
    /* do_something */
    ....

    /* 如果事件未发生(condition表达式不为真) taskA睡眠*/
    wait_event(wq, condition);
    
    /* do_something */
    ....
}

void taskB(void) {
    /* do_something */
    ....

    /* taskA等待的事件发生,唤醒taskA*/
    wake_up(wq, condition);
    
    /* do_something */
    ....
}

个人理解如果要浅层次的理解wait queue机制,可以简单概括为,在需要等待某个事件时,将任务挂载到等待链表上,然后使该任务随眠,切换至其他任务执行;当事件发生时,遍历该链表上的任务,将其一一从等待链表上摘除唤醒。这是wait queue机制的骨架,至于超时唤醒,条件判断等机制都是在该骨架上衍生而来。

1 数据结构

struct wait_queue_head {
	spinlock_t		lock;
	struct list_head	head;
};

struct wait_queue_entry {
    /* 配置标志 */
	unsigned int		flags;
    /* task_struct */
	void			*private;
    /* 唤醒函数 */
	wait_queue_func_t	func;
	struct list_head	entry;
};

wait_queue_head这个数据结构很简单,就是一个带锁的链表,wait_queue机制用这个链表来代表一个事件,所有等待这个事件的任务都会用wait_queue_entry结构体表示,并挂载到这个链表上

2 wait_event()

#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)		\
({										\
	__label__ __out;							\
	struct wait_queue_entry __wq_entry;					/* (1) */ \
	long __ret = ret;	/* explicit shadow */				\
										\
	init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);	\
	for (;;) {								\
		long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state); /* (2) */\
										\
		if (condition)		/* (3) */					\
			break;							\
										\
		if (___wait_is_interruptible(state) && __int) {			/* (4) */ \
			__ret = __int;						\
			goto __out;						\
		}								\
										\
		cmd; /* (5) */								\
	}									\
	finish_wait(&wq_head, &__wq_entry);				(6)	\
__out:	__ret;									\
})

以wait_event为例,当使用wait_event(wq_head, condition),最终会调用___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0, schedule())

(1) 定义一个wait_queue_entry, 并使用init_wait_entry进行初始化;
(2) 调用prepare_to_wait_event()将wait_queue_entry挂入链表,同时将当前任务状态设置为state(一般为TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE),准备修眠;
(3) 如果等待的条件已被满足,结束等待,跳出循环至(6);
(4) 如果在加入队列过程中进程(调用prepare_to_wait_event)睡眠状态被打断此处会结束等待过程;
(5) 对于wait_event接口,这个cmd是schedule(),此时任务被切走随眠
(6) 结束等待

从上述过程不难看出,等待事件condition的任务在等待条件被满足的过程中可能会被唤醒不只一次,condition不满足时,其会继续睡眠。如果这里的state被配置为TASK_INTERRUPTIBLE时,那么该进程被打断时,其会结束这个等待过程。

3 wake_up()

wake_up函数最终会调用__wake_up_common_lock(),这个函数的功能就是将等待链表上的任务一一唤醒,并将这些任务的wait_queue_entry从链表上摘下。这个函数里有一个比较有趣的机制,bookmark,正如其名书签,当wake的数目超过一个阈值WAITQUEUE_WALK_BREAK_CNT(默认是64)时,其会主动中断唤醒操作,尝试让出cpu,之后再从上次中断的地方继续唤醒。个人理解这是为了避免一次性唤醒的任务过多,一直占用CPU。

4 结语

上文分析了,最基本的wait_event()和wake_up()流程,其余诸如wait_event_timeout()等,大多类似,只在睡眠或唤醒时有一些细节上的差异,本次不再赘述,如果再发现有趣的点再补充吧。

----- Waiting Channels: pid 23820 at 2025-10-14 09:35:26.285758202+0800 ----- Cmd line: com.huawei.vassistantcar sysTid=23820 binder_thread_read sysTid=23825 do_sigtimedwait sysTid=23826 pipe_read sysTid=23827 do_sys_poll sysTid=23828 futex_wait_queue_me sysTid=23829 futex_wait_queue_me sysTid=23830 futex_wait_queue_me sysTid=23831 futex_wait_queue_me sysTid=23832 futex_wait_queue_me sysTid=23833 binder_thread_read sysTid=23834 futex_wait_queue_me sysTid=23835 binder_thread_read sysTid=23838 binder_thread_read sysTid=23839 binder_thread_read sysTid=23840 futex_wait_queue_me sysTid=23841 futex_wait_queue_me sysTid=23843 futex_wait_queue_me sysTid=23846 do_epoll_wait sysTid=23848 futex_wait_queue_me sysTid=23849 futex_wait_queue_me sysTid=23853 futex_wait_queue_me sysTid=23855 do_epoll_wait sysTid=23856 do_epoll_wait sysTid=23857 do_epoll_wait sysTid=23858 do_epoll_wait sysTid=23859 do_epoll_wait sysTid=23860 do_epoll_wait sysTid=23861 do_epoll_wait sysTid=23862 do_epoll_wait sysTid=23863 do_epoll_wait sysTid=23864 do_epoll_wait sysTid=23865 do_epoll_wait sysTid=23866 do_epoll_wait sysTid=23867 do_epoll_wait sysTid=23868 futex_wait_queue_me sysTid=23869 futex_wait_queue_me sysTid=23870 futex_wait_queue_me sysTid=23871 futex_wait_queue_me sysTid=23872 futex_wait_queue_me sysTid=23873 do_epoll_wait sysTid=23874 do_epoll_wait sysTid=23876 do_epoll_wait sysTid=23878 do_epoll_wait sysTid=23879 do_epoll_wait sysTid=23880 do_epoll_wait sysTid=23883 futex_wait_queue_me sysTid=23884 futex_wait_queue_me sysTid=23886 do_epoll_wait sysTid=23887 futex_wait_queue_me sysTid=23888 futex_wait_queue_me sysTid=23889 do_epoll_wait sysTid=23890 futex_wait_queue_me sysTid=23891 do_epoll_wait sysTid=23893 do_epoll_wait sysTid=23894 futex_wait_queue_me sysTid=23899 futex_wait_queue_me sysTid=23901 futex_wait_queue_me sysTid=23902 futex_wait_queue_me sysTid=23915 futex_wait_queue_me sysTid=23917 do_epoll_wait sysTid=23932 futex_wait_queue_me sysTid=23936 do_epoll_wait sysTid=23937 do_epoll_wait sysTid=23940 do_epoll_wait sysTid=23947 do_epoll_wait sysTid=23951 do_epoll_wait sysTid=23952 do_epoll_wait sysTid=23953 do_epoll_wait sysTid=23955 do_epoll_wait sysTid=23977 do_epoll_wait sysTid=23980 futex_wait_queue_me sysTid=23982 do_epoll_wait sysTid=23983 do_epoll_wait sysTid=23984 futex_wait_queue_me sysTid=23986 futex_wait_queue_me sysTid=23987 do_epoll_wait sysTid=23988 do_epoll_wait sysTid=23990 futex_wait_queue_me sysTid=23995 futex_wait_queue_me sysTid=23997 futex_wait_queue_me sysTid=23998 futex_wait_queue_me sysTid=24002 futex_wait_queue_me sysTid=24003 do_epoll_wait sysTid=24005 do_epoll_wait sysTid=24009 futex_wait_queue_me sysTid=24010 futex_wait_queue_me sysTid=24012 futex_wait_queue_me sysTid=24014 do_epoll_wait sysTid=24015 do_epoll_wait sysTid=24016 futex_wait_queue_me sysTid=24018 do_epoll_wait sysTid=24019 do_epoll_wait sysTid=24021 futex_wait_queue_me sysTid=24024 futex_wait_queue_me sysTid=24025 futex_wait_queue_me sysTid=24026 futex_wait_queue_me sysTid=24027 futex_wait_queue_me sysTid=24028 futex_wait_queue_me sysTid=24029 futex_wait_queue_me sysTid=24030 futex_wait_queue_me sysTid=24031 futex_wait_queue_me sysTid=24032 futex_wait_queue_me sysTid=24033 do_sys_poll sysTid=24035 binder_thread_read sysTid=24042 futex_wait_queue_me sysTid=24043 futex_wait_queue_me sysTid=24045 futex_wait_queue_me sysTid=24052 futex_wait_queue_me sysTid=24055 do_epoll_wait sysTid=24056 do_epoll_wait sysTid=24062 do_epoll_wait sysTid=24072 do_epoll_wait sysTid=24073 futex_wait_queue_me sysTid=24074 futex_wait_queue_me sysTid=24079 futex_wait_queue_me sysTid=24080 do_epoll_wait sysTid=24084 futex_wait_queue_me sysTid=24085 futex_wait_queue_me sysTid=24093 futex_wait_queue_me sysTid=24106 futex_wait_queue_me sysTid=24107 futex_wait_queue_me sysTid=24112 futex_wait_queue_me sysTid=24114 do_epoll_wait sysTid=24116 do_epoll_wait sysTid=24121 futex_wait_queue_me sysTid=24122 futex_wait_queue_me sysTid=24123 futex_wait_queue_me sysTid=24124 futex_wait_queue_me sysTid=24128 do_epoll_wait sysTid=24131 futex_wait_queue_me sysTid=24133 futex_wait_queue_me sysTid=24135 futex_wait_queue_me sysTid=24136 futex_wait_queue_me sysTid=24138 futex_wait_queue_me sysTid=24139 do_epoll_wait sysTid=24150 futex_wait_queue_me sysTid=24152 futex_wait_queue_me sysTid=24180 binder_thread_read sysTid=24199 futex_wait_queue_me sysTid=24211 do_epoll_wait sysTid=24236 futex_wait_queue_me sysTid=24250 futex_wait_queue_me sysTid=24251 futex_wait_queue_me sysTid=24288 futex_wait_queue_me sysTid=24290 sk_wait_data sysTid=24291 sk_wait_data sysTid=24292 futex_wait_queue_me sysTid=24294 futex_wait_queue_me sysTid=24309 futex_wait_queue_me sysTid=24324 futex_wait_queue_me sysTid=24325 futex_wait_queue_me sysTid=24357 futex_wait_queue_me sysTid=24374 do_epoll_wait sysTid=24441 futex_wait_queue_me sysTid=24459 futex_wait_queue_me sysTid=24477 futex_wait_queue_me sysTid=24478 futex_wait_queue_me sysTid=24479 futex_wait_queue_me sysTid=24484 futex_wait_queue_me sysTid=24485 futex_wait_queue_me sysTid=24486 futex_wait_queue_me sysTid=24489 do_epoll_wait sysTid=24490 futex_wait_queue_me sysTid=24493 do_epoll_wait sysTid=24531 futex_wait_queue_me sysTid=24532 futex_wait_queue_me sysTid=24592 futex_wait_queue_me sysTid=24595 do_epoll_wait sysTid=24596 futex_wait_queue_me sysTid=24614 binder_thread_read sysTid=24625 binder_thread_read sysTid=24628 binder_thread_read sysTid=24629 futex_wait_queue_me sysTid=24632 binder_thread_read sysTid=24635 futex_wait_queue_me sysTid=24728 futex_wait_queue_me sysTid=24741 futex_wait_queue_me sysTid=24743 futex_wait_queue_me sysTid=24745 futex_wait_queue_me sysTid=24746 futex_wait_queue_me sysTid=24747 futex_wait_queue_me sysTid=24748 futex_wait_queue_me sysTid=24749 futex_wait_queue_me sysTid=24752 futex_wait_queue_me sysTid=24758 futex_wait_queue_me sysTid=24764 futex_wait_queue_me sysTid=24771 futex_wait_queue_me sysTid=24797 futex_wait_queue_me sysTid=24799 futex_wait_queue_me sysTid=24800 futex_wait_queue_me sysTid=24801 futex_wait_queue_me sysTid=24802 futex_wait_queue_me sysTid=24804 futex_wait_queue_me sysTid=24808 futex_wait_queue_me sysTid=24821 do_epoll_wait sysTid=24822 futex_wait_queue_me sysTid=24823 futex_wait_queue_me sysTid=24824 futex_wait_queue_me sysTid=25059 futex_wait_queue_me sysTid=25219 binder_thread_read 这个是啥
最新发布
10-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值