背景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器
- 使用工具:Source Insight 3.5, Visio
1. 介绍
在Linux OS中,Idle进程的运行会让CPU进入cpuidle状态。当没有其他进程处于运行状态时,Scheduler会选择Idle进程来运行,此时CPU无所事事。
在ARM64架构中,当CPU Idle时,会调用WFI
指令(wait for interrupt
),关掉CPU的Clock以便降低功耗,当有外设中断触发时,CPU又会恢复回来。
cpuidle framework
就是用来向上给Scheduler/Sysfs
提供使用接口,向下用来对接不同架构的处理器,凡是框架基本都大同小异,屏蔽硬件层并抽象使用接口。
相信你已经猜到了,cpuidle和电源管理相关。
2. 框架
代码路径:
driver/cpuidle/cpuidle.c
driver/cpuidle/driver.c
driver/cpuidle/governor.c
driver/cpuidle/sysfs.c
kernel/shced/idle.c
老规矩,上图:
简单说明一下吧:调度器发现没有Task处在运行状态时,切换到Idle进程,此时通过cpuidle_idle_call
接口调到cpuidle framework
,cpuidle framework
会选择合适的策略来决定进入哪种状态,最终回调到底层的平台实现。
SMP处理器
都有cpuidle状态,而各个状态下的功耗都不同,是否进入cpuidle状态有两个重要的参考因素:
- CPU
进入-退出
cpuidle状态的latency; - CPU处在cpuidle状态的功耗;
Latency和功耗的tradeoff,是需要根据实际情况来选择策略的,也就是Governor的作用。
3. 数据结构
cpuidle core
抽象出了三个数据结构:
cpuidle device
:用于描述CPU核;cpuidle driver
: 针对CPU核的驱动;cpuidle governor
:主要根据cpuidle的device和driver状态来选择策略;
图如下:
3.1 cpuidle device
针对每个CPU核都对应一个struct cpuidle_device
结构,主要字段介绍如下:
registered
:该cpu核是否注册进内核中;enabled
:该cpu核是否已经使能;cpu
:对应的cpu number;last_residency
:该cpu核上一次停留在cpuidle状态的时间(us);state_count
:cpuidle状态的个数;states_usage
:struct cpuidle_state_usage
数组,记录每个cpuidle状态的统计信息,包括是否使能、进入该cpuidle状态的次数,停留在该cpuidle状态的总时间(us);kobjs*
:与sysfs组织相关,开发给用户层来操作底层;device_list
:全局链表,链接到cpuidle_detected_device
上;
3.2 cpuidle driver
cpuidle driver
用于驱动一个或多个CPU核,关键字段描述如下:
bctimer
:用于驱动注册时判断是否需要设置broadcast timer;states[]
:struct cpuidle_state
数组,用于描述cpuidle的状态,需要按照功耗从大到小来排序,具体有多少个cpuidle状态,取决于device Tree中的定义,默认已经有state[0]
,如上图所示。cpumask
:用于表明支持哪些CPU核;
struct cpuidle_state
中的enter
函数,是最终进入cpuidle状态的函数。不同处理器的cpuidle驱动实现,主要是填充state
结构体。
3.3 cpuidle governor
governor结构主要提供不同的回调函数,最终由menu_governor
填充,主要字段如下:
enable/disable
:在设备驱动注册和注销的时候调用;select
:根据已有状态来选择一个cpuidle状态;reflect
:调用该接口告知governor,CPU上一次所处的cpuidle状态是哪个;
流程
以cpuidle-arm.c
为例,整个注册流程如下图:
注册之后便将设备和驱动建立起连接关系了,最终cpuidle framework
的用户便可通过接口来调用下层的接口,进而完成具体的硬件操作。
Idle Task通过cpuidle_enter
为入口,调用到cpuidle_framework
,流程如下图:
Idle Task调用cpuidle_enter
之前,需要先通过governor
来运用策略来选择将要进入的cpuidle state。入口为cpuidle_select
,当完成状态切换后会调用cpuidle_reflect
来将信息更新到governor
。具体的图如下:
其中Governor
关于状态的策略选择,可以参考menu.c
的注释,主要有三个决定因素:
- 功耗平衡点,也就是需要权衡考虑cpuidle状态带来的功耗节省和在该cpuidle状态下的停留时间,假如停留时间太短(小于
target_residency
),则不划算。 - 性能影响,那些具有大的延迟退出(
exit_latency
)的cpuidle state
,通常会对工作负载产生较大影响,这个对系统管理员来说是不可接受的。此外,低性能往往也意味着低功耗。 - 延迟容忍度(从
pmqos
框架获取),在满足延迟容忍度latency_req
的条件下,选择功耗最小的cpuidle状态。
具体的策略不再分析,请直接看driver/cpuilde/menu.c
代码及注释。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。