时间:2023-03-09来源:系统城装机大师作者:佚名
内核有个函数 wake_up 和 wake_up_interruptible 通常来说看到这俩函数调用就是唤醒等待队列上的线程。
直到看了epoll的源码,发现并非如此。
1 2 3 4 5 6 7 8 9 10 |
bool wakeup_condition; wait_queue_head_t wait_queue; init_waitqueue_head(&wait_queue); wait_queue_entry_t wq_entry // wait wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop()); // 唤醒 // 设置等待条件为true,并唤醒 wakeup_condition = true ; wake_up(&wait_queue); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
// common/include/linux/wait.h #define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE) #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) // common/kernel/sched/wait.c // wake_up 是个宏,展开后调用的是 __wake_up 函数 // __wake_up(x, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, NULL) int __wake_up( struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, void *key) { return __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key); } EXPORT_SYMBOL(__wake_up); // __wake_up_common_lock(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL) static int __wake_up_common_lock( struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { unsigned long flags; wait_queue_entry_t bookmark; int remaining = nr_exclusive; bookmark.flags = 0; bookmark. private = NULL; bookmark.func = NULL; INIT_LIST_HEAD(&bookmark.entry); //初始化链表: 链表的next和prev指针都指向链表自身地址 do { spin_lock_irqsave(&wq_head->lock, flags); //自旋锁上锁,对队列上锁 remaining = __wake_up_common(wq_head, mode, remaining, wake_flags, key, &bookmark); spin_unlock_irqrestore(&wq_head->lock, flags); //自旋锁解锁 } while (bookmark.flags & WQ_FLAG_BOOKMARK); return nr_exclusive - remaining; //队列为空时,remaining=nr_exclusive ,此时 return 0; } // __wake_up_common(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL, &bookmark); static int __wake_up_common( struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, int wake_flags, void *key, wait_queue_entry_t *bookmark) { wait_queue_entry_t *curr, *next; int cnt = 0; lockdep_assert_held(&wq_head->lock); // bookmark.flags = 0; WQ_FLAG_BOOKMARK = 0x04; if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) { //不会进入此分支 curr = list_next_entry(bookmark, entry); list_del(&bookmark->entry); bookmark->flags = 0; } else curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry); //获取wq_head队列的第一个元素 if (&curr->entry == &wq_head->head) //队列为空时,直接返回传入的 nr_exclusive return nr_exclusive; list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) { //遍历链表 unsigned flags = curr->flags; int ret; if (flags & WQ_FLAG_BOOKMARK) continue ; /* 调用 wait_queue_entry_t 中的回调函数 func // 这里依据func的类型会出现不同的结果。 使用 init_waitqueue_entry 初始化的 wait_queue_entry_t ,func = default_wake_function,这个函数会唤醒 curr->private 上的线程。 使用 init_waitqueue_func_entry 初始化的 wait_queue_entry_t,仅仅是做普通的函数调用。 */ ret = curr->func(curr, mode, wake_flags, key); if (ret < 0) break ; if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break ; if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) && (&next->entry != &wq_head->head)) { bookmark->flags = WQ_FLAG_BOOKMARK; list_add_tail(&bookmark->entry, &next->entry); break ; } } return nr_exclusive; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//内核4.14以后 // common/include/linux/wait.h struct wait_queue_head { // wait队列 spinlock_t lock; // 自旋锁 struct list_head head; // 添加到 wait 队列时,就是把wait_queue_entry.entry 加入这个 head 链表 }; /* * A single wait-queue entry structure: */ struct wait_queue_entry { // wait队列的一个项 unsigned int flags; void * private ; // 私有数据,在init_waitqueue_entry中代表线程,在init_waitqueue_func_entry中为null wait_queue_func_t func; // 回调函数 struct list_head entry; // 添加到 wait 队列时,就是把这个 entry 加入到 wait_queue_head.head 的链表 }; typedef struct wait_queue_head wait_queue_head_t; // wait_queue_head_t 同 wait_queue_head typedef struct wait_queue_entry wait_queue_entry_t; // wait_queue_entry_t 同 wait_queue_entry |
对于 wait_queue_entry
有两种常用的初始化方法 init_waitqueue_entry
和 init_waitqueue_func_entry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// common/include/linux/wait.h static inline void init_waitqueue_entry( struct wait_queue_entry *wq_entry, struct task_struct *p) { wq_entry->flags = 0; wq_entry-> private = p; // 把需要唤醒的线程存储到 private 数据中 // func 赋值为 default_wake_function 函数 // 这个函数的作用是 唤醒等待队列上的线程 wq_entry->func = default_wake_function; // 这函数作用是:唤醒线程 p } static inline void init_waitqueue_func_entry( struct wait_queue_entry *wq_entry, wait_queue_func_t func) { wq_entry->flags = 0; wq_entry-> private = NULL; wq_entry->func = func; // 直接把传入的回调函数赋值给 wq_entry->func } |
这个函数的作用基本等效于 wake_up_process
函数。
1 2 3 4 5 6 7 8 9 |
int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags, void *key) { WARN_ON_ONCE(IS_ENABLED(CONFIG_SCHED_DEBUG) && wake_flags & ~WF_SYNC); //try_to_wake_up函数通过把进程状态设置为TASK_RUNNING, 并把该进程插入本地CPU运行队列rq来达到唤醒睡眠和停止的进程的目的. // curr->private 存储了需要唤醒的线程 return try_to_wake_up(curr-> private , mode, wake_flags); } EXPORT_SYMBOL(default_wake_function); |
wake_up的两种用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
bool wakeup_condition; wait_queue_head_t wait_queue; init_waitqueue_head(&wait_queue); wait_queue_entry_t wq_entry // wait 第一种用法:线程等待 wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop()); 第二种用法:添加一个回调到等待队列上 init_waitqueue_func_entry(&wq_entry, callback); add_wait_queue(&wait_queue, &wq_entry); // 唤醒 设置等待条件为 true ,并唤醒 wakeup_condition = true ; // 内部遍历队列,调用每个 wait_queue_entry 的 func 函数,根据func不同为产生不同效果 wake_up(&wait_queue); |
注: 基于 内核4.14 以后版本分析,更多关于Android内核代码wake_up的资料请关注脚本之家其它相关文章!
2024-07-07
Java框架如何实现非阻塞式编程?2023-03-11
Android Jetpack 组件LiveData源码解析2023-03-11
hbuilderx设置Firefox浏览器安装路径教程 hbuilderx怎么设置Firefox浏览器安装路径?一、AVL树的概念 二、AVL树节点的定义 三、AVL树的插入 四、AVL树的旋转 1.左单旋 2.右单旋 3.左右双旋 4.右左双旋 五、进行验证 六、AVLTree的性能...
2023-03-09