Android 内核代码 wake_up源码解析

目录
  • 内核中通常用法:
  • wake_up 的源码:
  • func 赋值过程
    • wait_queue_head 和 wait_queue_entry 数据结构
    • 两种等待任务 wait_queue_entry:线程 和 函数
    • default_wake_function 函数
  • 综上:

内核中通常用法:

内核有个函数 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的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android源码解析onResume方法中获取不到View宽高

    目录 前言 问题1.为什么onCreate和onResume中获取不到view的宽高? 问题2.为什么View.post为什么可以获取View宽高? 结论 前言 有一个经典的问题,我们在Activity的onCreate中可以获取View的宽高吗?onResume中呢? 对于这类八股问题,只要看过都能很容易得出答案:不能. 紧跟着追问一个,那为什么View.post为什么可以获取View宽高? 今天来看看这些问题,到底为何? 今日份问题: 为什么onCreate和onResume中获取不到vie

  • Android应用程序保持后台唤醒(使用WakeLock实现)

    在使用一些产品列如微信.QQ之类的,如果有新消息来时,手机屏幕即使在锁屏状态下也会亮起并提示声音,这时用户就知道有新消息来临了.但是,一般情况下手机锁屏后,Android系统为了省电以及减少CPU消耗,在一段时间后会使系统进入休眠状态,这时,Android系统中CPU会保持在一个相对较低的功耗状态.针对前面的例子,收到新消息必定有网络请求,而网络请求是消耗CPU的操作,那么如何在锁屏状态乃至系统进入休眠后,仍然保持系统的网络状态以及通过程序唤醒手机呢?答案就是Android中的WakeLock机

  • android WakeLock使用方法代码实例

    Android中提供了一个名为WakeLock的类在android.os.PowerManager.WakeLock中,从名字来看WakeLock是唤醒锁的意思,它可以控制屏幕的背光开关,所以在电源管理类. WakeLock实例化方法比较简单,因为是系统的远程服务,通过下面的代码来构造 复制代码 代码如下: PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock

  • Android音视频开发Media FrameWork框架源码解析

    目录 一.Media FrameWork背景 二.Media Framework“路线图” 2.1 代理端 2.2 服务端 2.2.1 Source 2.2.2 Decoder 2.2.3 Renderer 2.2.4 Foundation 2.3 OMX端 2.4 Kernel端 三.media播放的流程 四.Media FrameWork源码分析 一.Media FrameWork背景 Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视

  • 详解Android获取系统内核版本的方法与实现代码

    Android获取系统内核版本的方法 这里主要实现获取Android Linux 内核的版本号,网上关于这类文章不是很多,这里记录下,希望能帮助到大家, 实现代码: public static String getKernelVersion() { String kernelVersion = ""; InputStream inputStream = null; try { inputStream = new FileInputStream("/proc/version&q

  • Android 内核代码 wake_up源码解析

    目录 内核中通常用法: wake_up 的源码: func 赋值过程 wait_queue_head 和 wait_queue_entry 数据结构 两种等待任务 wait_queue_entry:线程 和 函数 default_wake_function 函数 综上: 内核中通常用法: 内核有个函数 wake_up 和 wake_up_interruptible 通常来说看到这俩函数调用就是唤醒等待队列上的线程. 直到看了epoll的源码,发现并非如此. bool wakeup_conditi

  • Android文件存储SharedPreferences源码解析

    1.我们都知道SharedPreferences 是android可以用来存放key value的的文件. SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); editor.putString("key","value"); editor.commit(

  • Android Jetpack 组件LiveData源码解析

    目录 前言 基本使用 疑问 源码分析 Observer ObserverWrapper LifecycleBoundObserver MutableLiveData postValue setValue 问题答疑 LiveData 特性引出的问题 问题解决 最后 前言 本文来分析下 LiveData 的源码,以及其在实际开发中的一些问题. 基本使用 一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewMo

  • Android AsyncTask使用以及源码解析

    综述 在Android中,我们需要进行一些耗时的操作,会将这个操作放在子线程中进行.在子线程操作完成以后我们可以通过Handler进行发送消息,通知UI进行一些更新操作(具体使用及其原理可以查看Android的消息机制--Handler的工作过程这篇文章).当然为了简化我们的操作,在Android1.5以后为我们提供了AsyncTask类,它能够将子线程处理完成后的结果返回到UI线程中,之后我们便可以根据这些结果进行一列的UI操作了. AsyncTask的使用方法 实际上AsyncTask内部也

  • Android跑马灯MarqueeView源码解析

    跑马灯效果,大家可以去原作者浏览https://github.com/sfsheng0322/MarqueeView 下面看自定义控件的代码 public class MarqueeView extends ViewFlipper { private Context mContext; private List<String> notices; private boolean isSetAnimDuration = false; private OnItemClickListener onIt

  • Android 中 SwipeLayout一个展示条目底层菜单的侧滑控件源码解析

    由于项目上的需要侧滑条目展示收藏按钮,记得之前代码家有写过一个厉害的开源控件 AndroidSwipeLayout 本来准备直接拿来使用,但是看过 issue 发现现在有不少使用者反应有不少的 bug ,而且代码家现在貌似也不进行维护了.故自己实现了一个所要效果的一个控件.因为只是实现我需要的效果,所以大家也能看到,代码里有不少地方我是写死的.希望对大家有些帮助.而且暂时也不需要 AndroidSwipeLayout 大而全的功能,算是变相给自己做的项目精简代码了. 完整示例代码请看:GitHu

  • Android源码解析之截屏事件流程

    今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程.用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了).那么这里的截屏事件是如何触发的呢?触发之后android系统是如何实现截屏操作的呢?带着这两个问题,开始我们的源码阅读流程. 我们知道这里的截屏事件是通过我们的按键操作触发的,所以这里就需要我们从android系统的按键触发模块开始看起,由于我们在不同的App页面,操作音量减少键和电

  • Android okhttp的启动流程及源码解析

    前言 这篇文章主要讲解了okhttp的主要工作流程以及源码的解析. 什么是OKhttp 简单来说 OkHttp 就是一个客户端用来发送 HTTP 消息并对服务器的响应做出处理的应用层框架. 那么它有什么优点呢? 易使用.易扩展. 支持 HTTP/2 协议,允许对同一主机的所有请求共用同一个 socket 连接. 如果 HTTP/2 不可用, 使用连接池复用减少请求延迟. 支持 GZIP,减小了下载大小. 支持缓存处理,可以避免重复请求. 如果你的服务有多个 IP 地址,当第一次连接失败,OkHt

  • 解析Android框架之OkHttp3源码

    OkHttp流程图 OkHttp基本使用 gradle依赖 implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.15.0' /** *这里拿get请求来 * 异步的get请求 */ public void okhttpAsyn() { //设置超时的时间 OkHttpClient.Builder builder = new OkHttpClient.Builder

  • 解析Android框架之Volley源码

    Volley简单使用 我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖. 好了,接下来上代码了..... //获取volley的请求对象 RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext()); StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com

随机推荐