Android 内核代码 wake_up源码解析

 更新时间:2023年03月08日 10:49:34   作者:拉普  
这篇文章主要为大家介绍了Android 内核代码 wake_up源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

内核中通常用法:

内核有个函数 wake_up 和 wake_up_interruptible 通常来说看到这俩函数调用就是唤醒等待队列上的线程。

直到看了epoll的源码,发现并非如此。

    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);

wake_up 的源码:

// 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;
}

func 赋值过程

wait_queue_head 和 wait_queue_entry 数据结构

//内核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_entryinit_waitqueue_func_entry

两种等待任务 wait_queue_entry:线程 和 函数

// common/include/linux/wait.h
static inline void init_waitqueue_entry(struct wait_queue_entry *wq_entry, struct task_struct *p)
{
	wq_entry-&gt;flags		= 0;
	wq_entry-&gt;private	= p; // 把需要唤醒的线程存储到 private 数据中
    // func 赋值为 default_wake_function 函数
    // 这个函数的作用是 唤醒等待队列上的线程
	wq_entry-&gt;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-&gt;flags		= 0;
	wq_entry-&gt;private	= NULL;
	wq_entry-&gt;func		= func; // 直接把传入的回调函数赋值给 wq_entry-&gt;func
}

default_wake_function 函数

这个函数的作用基本等效于 wake_up_process 函数。

int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags,
			  void *key)
{
	WARN_ON_ONCE(IS_ENABLED(CONFIG_SCHED_DEBUG) &amp;&amp; wake_flags &amp; ~WF_SYNC);
    //try_to_wake_up函数通过把进程状态设置为TASK_RUNNING, 并把该进程插入本地CPU运行队列rq来达到唤醒睡眠和停止的进程的目的.
    // curr-&gt;private 存储了需要唤醒的线程
	return try_to_wake_up(curr-&gt;private, mode, wake_flags);
}
EXPORT_SYMBOL(default_wake_function);

综上:

  • wake_up ,可能是唤醒队列上的线程,也可能仅仅是触发一个回调而已

wake_up的两种用法:

    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的资料请关注脚本之家其它相关文章!

相关文章

  • Android FFmpeg音视频解码播放示例详解

    Android FFmpeg音视频解码播放示例详解

    这篇文章主要为大家介绍了Android FFmpeg音视频解码播放示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Android 个人理财工具三:添加账单页面 上

    Android 个人理财工具三:添加账单页面 上

    本文主要介绍Android 个人理财工具添加账单页面的功能实现,这里提供实例代码和实现效果图,有兴趣的小伙伴可以参考下
    2016-08-08
  • Android编程判断手机上是否安装了某个程序的方法

    Android编程判断手机上是否安装了某个程序的方法

    这篇文章主要介绍了Android编程判断手机上是否安装了某个程序的方法,涉及Android针对程序包的操作及进程判断的相关技巧,需要的朋友可以参考下
    2015-11-11
  • Android系统关机的全流程解析

    Android系统关机的全流程解析

    这篇文章主要介绍了Android系统关机的全流程解析,从上层空间一直深入到内核全面讲解,非常推荐!需要的朋友可以参考下
    2016-02-02
  • 关于OkHttp中response.body().string()的用法解析

    关于OkHttp中response.body().string()的用法解析

    这篇文章主要介绍了关于OkHttp中response.body().string()的用法解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Android自定义录制视频功能

    Android自定义录制视频功能

    这篇文章主要为大家详细介绍了Android自定义录制视频功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • 另外两种Android沉浸式状态栏实现思路

    另外两种Android沉浸式状态栏实现思路

    这篇文章主要为大家介绍了另外两种Android沉浸式状态栏实现思路,android5.0及以后版本都支持给状态栏着色,而目前android主流版本还是4.4,想要深入了解的朋友可以参考一下
    2016-01-01
  • Android 微信摇骰子和猜拳作弊器原理解析

    Android 微信摇骰子和猜拳作弊器原理解析

    这篇文章主要介绍了Android 微信摇骰子和猜拳作弊器原理解析的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • Android context源码详解及深入分析

    Android context源码详解及深入分析

    这篇文章主要介绍了Android context源码详解及深入分析的相关资料,这里对Android Context 如何使用进行了详细介绍,需要的朋友可以参考下
    2017-01-01
  • Android实现蓝牙串口通讯

    Android实现蓝牙串口通讯

    这篇文章主要为大家详细介绍了Android实现蓝牙串口通讯,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08

最新评论