我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android内核代码wake_up源码解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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;

		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 链表
};

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的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android内核代码wake_up源码解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android内核代码wake_up源码解析

这篇文章主要为大家介绍了Android内核代码wake_up源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-08

Android内核wake_up源码分析

今天小编给大家分享一下Android内核wake_up源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。内核中通常用法:
2023-07-05

Android ArrayMap源代码分析

分析源码之前先来介绍一下ArrayMap的存储结构,ArrayMap数据的存储不同于HashMap和SparseArray。Java提供了HashMap,但是HashMap对于手机端而言,对空间的利用太大,所以Android提供
2022-06-06

如何分析Linux内核源码do_fork

本篇文章为大家展示了如何分析Linux内核源码do_fork,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。我们都知道进程是Linux内核中最为重要的一个抽象概念,那么我们平时在fork一个进程时,该
2023-06-16

JavaCountDownLatch的源码硬核解析

对于并发执行,Java中的CountDownLatch是一个重要的类。为了更好的理解CountDownLatch这个类,本文将通过例子和源码带领大家深入解析这个类的原理,感兴趣的可以学习一下
2022-11-13

openSUSE如何给内核源代码打补丁

小编给大家分享一下openSUSE如何给内核源代码打补丁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!内核源码升级可能使某个补丁失效,所以并不是一个补丁可以"补"
2023-06-16

Linux内核网络协议栈源码剖析

Linux内核网络协议栈是一个非常庞大和复杂的软件系统,涉及到很多不同的模块和功能。以下是一个大致的剖析步骤:1. 网络协议栈的初始化:从内核启动开始,网络协议栈的初始化是一个非常重要的步骤。在这个过程中,会初始化各种网络协议的数据结构,如
2023-09-23

linux内核中list链表的源码分析

这篇文章将为大家详细讲解有关linux内核中list链表的源码分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。linux kernel里的很多数据结构都很经典, list链表就是其中之一,
2023-06-06

Ubuntu Android源码以及内核下载与编译

本教程是基于Ubuntu下Android6.0.1源码以及内核的下载和编译,记录一下,以后也就不用自己去找资料,一遍一遍的尝试了。可以翻墙的,英语好的,直接去AndroidSource. 系统环境:Ubuntu14.04LTSAndroid
2022-06-06

Android内核源码 在Ubuntu上下载,编译,安装

从源代码树下载下来的最新Android源代码,是不包括内核代码的,也就是Android源代码工程默认不包含Linux Kernel代码,而是使用预先编译好的内核,也就是prebuilt/android-arm/kernel/kernel-q
2022-06-06

Linux内核源代码编程的规范有哪些

这篇文章主要介绍“Linux内核源代码编程的规范有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Linux内核源代码编程的规范有哪些”文章能帮助大家解决问题。1.强烈推荐单行的宽度为八十列。任何
2023-06-27

linux内核的源代码放在什么文件下

这篇文章主要介绍了linux内核的源代码放在什么文件下的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇linux内核的源代码放在什么文件下文章都会有所收获,下面我们一起来看看吧。li
2023-04-20

分析Linux内核调度器源码之初始化

目录一、导语二、调度器的基本概念2.1、运行队列(rq)2.2、调度类(sched_class)2.3、调度域(sched_domain)2.4、调度组(sched_group)2.5、根域(root_domain)2.6、组调度(grou
2022-06-03

Android Jetpack 组件LiveData源码解析

这篇文章主要为大家介绍了Android Jetpack 组件LiveData源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-10

Android跑马灯MarqueeView源码解析

跑马灯效果,大家可以去原作者浏览https://github.com/sfsheng0322/MarqueeView 下面看自定义控件的代码public class MarqueeView extends ViewFlipper {pri
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录