文章目录
一、数据结构
1.1 等待队列头
34struct wait_queue_head {
35 spinlock_t lock;
36 struct list_head head;
37};
1.2 等待队列成员
27struct wait_queue_entry {
28 unsigned int flags;
//私有数据,一般指向task_struct结构体
29 void *private;
//这个函数可以唤醒等待进程
30 wait_queue_func_t func;
//把自己插入链表需要这个
31 struct list_head entry;
32};
二、函数
2.1 进程等待
等待事件的函数:
wait_event:这个里面进程的状态会被设置成TASK_UNINTERRUPTIBLE
wait_event_interruptible:这个函数里面进程会被设置为TASK_INTERRUPTIBLE
wait_event_interruptible_timeout:这个是带有超时的等待
2.1.1 wait_event
调用这个函数的进程将加入等待队列,函数定义为定义为:
//第一个参数就是进程要加入的等待队列的头
//第二个参数是等待的条件:例如a!=b。
#define wait_event(wq_head, condition) \
do {
//CONFIG_DEBUG_ATOMIC_SLEEP内核选项不打开的情况下,什么也不做 \
might_sleep();
//如果条件满足了,啥也不干就break,条件没满足就下一步__wait_event; \
if (condition) \
break; \
__wait_event(wq_head, condition); \
} while (0)
2.1.2 __wait_event:
#define __wait_event(wq_head, condition) \
//最后一个参数是schedule()函数,调用这个函数放弃CPU
(void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0, \
schedule())
___wait_event:
259#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd) \
260({ \
261 __label__ __out; \
262 struct wait_queue_entry __wq_entry; \
263 long __ret = ret; /* explicit shadow */ \
264 \
//初始化等待队列的成员,等待队列的成员
//函数里面有一句:wq_entry->private = current,private指向当前进程的进程描述符
//这个函数里面指定了等待队列的唤醒函数为autoremove_wake_function
265 init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \
266 for (;;) { \
//调用这个函数,改变进程状态
267 long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
268 \
//被唤醒了就break
269 if (condition) \
270 break; \
271 \
272 if (___wait_is_interruptible(state) && __int) { \
273 __ret = __int; \
274 goto __out; \
275 } \
276 \
//这个就是schedule()函数,到这里进程放弃了CPU
277 cmd; \
278 } \
//将当前进程的状态设置为:TASK_RUNNING
279 finish_wait(&wq_head, &__wq_entry); \
280__out: __ret; \
281})
2.1.3 prepare_to_wait_event:
258long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)
259{
260 unsigned long flags;
261 long ret = 0;
262
263 spin_lock_irqsave(&wq_head->lock, flags);
264 if (signal_pending_state(state, current)) {
265 /*
266 * Exclusive waiter must not fail if it was selected by wakeup,
267 * it should "consume" the condition we were waiting for.
268 *
269 * The caller will recheck the condition and return success if
270 * we were already woken up, we can not miss the event because
271 * wakeup locks/unlocks the same wq_head->lock.
272 *
273 * But we need to ensure that set-condition + wakeup after that
274 * can't see us, it should wake up another exclusive waiter if
275 * we fail.
276 */
277 list_del_init(&wq_entry->entry);
278 ret = -ERESTARTSYS;
279 } else {
280 if (list_empty(&wq_entry->entry)) {
281 if (wq_entry->flags & WQ_FLAG_EXCLUSIVE)
282 __add_wait_queue_entry_tail(wq_head, wq_entry);
283 else
284 __add_wait_queue(wq_head, wq_entry);
285 }
286 set_current_state(state);
287 }
288 spin_unlock_irqrestore(&wq_head->lock, flags);
289
290 return ret;
291}
- 286行调用了set_current_state(state),设置当前进程的状态,进程的状态在__wait_event里面传递进来的,为:TASK_UNINTERRUPTIBLE,不可中断的睡眠(也就是不能被信号唤醒,只能在等待的事件发生后唤醒)。
2.2 add_wait_queue
18void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
19{
20 unsigned long flags;
21
22 wq_entry->flags &= ~WQ_FLAG_EXCLUSIVE;
23 spin_lock_irqsave(&wq_head->lock, flags);
24 __add_wait_queue(wq_head, wq_entry);
25 spin_unlock_irqrestore(&wq_head->lock, flags);
26}
下面看__add_wait_queue的定义:
167static inline void __add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
168{
169 list_add(&wq_entry->entry, &wq_head->head);
170}
2.2进程唤醒
2.2.1wake
函数定义:
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
//第一个参数就是等待队列的头
139void __wake_up(struct wait_queue_head *wq_head, unsigned int mode,
140 int nr_exclusive, void *key)
141{
142 __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
143}
110static void __wake_up_common_lock(struct wait_queue_head *wq_head, unsigned int mode,
111 int nr_exclusive, int wake_flags, void *key)
112{
113 unsigned long flags;
114 wait_queue_entry_t bookmark;
115
116 bookmark.flags = 0;
117 bookmark.private = NULL;
118 bookmark.func = NULL;
119 INIT_LIST_HEAD(&bookmark.entry);
120
121 do {
122 spin_lock_irqsave(&wq_head->lock, flags);
123 nr_exclusive = __wake_up_common(wq_head, mode, nr_exclusive,
124 wake_flags, key, &bookmark);
125 spin_unlock_irqrestore(&wq_head->lock, flags);
126 } while (bookmark.flags & WQ_FLAG_BOOKMARK);
127}
128
66static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
67 int nr_exclusive, int wake_flags, void *key,
68 wait_queue_entry_t *bookmark)
69{
70 wait_queue_entry_t *curr, *next;
71 int cnt = 0;
72
73 lockdep_assert_held(&wq_head->lock);
74
75 if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) {
76 curr = list_next_entry(bookmark, entry);
77
78 list_del(&bookmark->entry);
79 bookmark->flags = 0;
80 } else
81 curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry);
82
83 if (&curr->entry == &wq_head->head)
84 return nr_exclusive;
85//遍历睡眠链表,调用唤醒函数
86 list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {
87 unsigned flags = curr->flags;
88 int ret;
89
90 if (flags & WQ_FLAG_BOOKMARK)
91 continue;
92//调用等待队列成员的fun函数
93 ret = curr->func(curr, mode, wake_flags, key);
94 if (ret < 0)
95 break;
96 if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
97 break;
98
99 if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
100 (&next->entry != &wq_head->head)) {
101 bookmark->flags = WQ_FLAG_BOOKMARK;
102 list_add_tail(&bookmark->entry, &next->entry);
103 break;
104 }
105 }
106
107 return nr_exclusive;
108}
三、唤醒函数
2.1.2中:___wait_event函数里面调用了:
init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);
这个函数定义如下:
249void init_wait_entry(struct wait_queue_entry *wq_entry, int flags)
250{
251 wq_entry->flags = flags;
252 wq_entry->private = current;
253 wq_entry->func = autoremove_wake_function;//唤醒函数
254 INIT_LIST_HEAD(&wq_entry->entry);
255}
370int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
371{
//内核默认的唤醒函数
372 int ret = default_wake_function(wq_entry, mode, sync, key);
373
//把相应的等待队列的元素从链表中删除
374 if (ret)
375 list_del_init(&wq_entry->entry);
376
377 return ret;
378}
//第一个参数是等待队列的元素
//第二个参数是进程状态,只有处于这个状态的进程才能被唤醒
//第三个参数是唤醒方式:0代表同步唤醒,1代表非同步唤醒
//第四个参数是唤醒时执行的函数
//由于wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)传递的key是NULL,所以wake_up唤醒后不执行任何函数
4336int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags,
4337 void *key)
4338{
4339 return try_to_wake_up(curr->private, mode, wake_flags);
4340}
try_to_wake_up这个函数比较长,分析其重要的部分:
(1)设置进程的状态为:TASK_RUNNING
(2)把进程插入本地cpu的运行队列