电源管理入门-CPU Idle有什么用?Idle状态判断

电子说

1.2w人已加入

描述

1. CPU Idle有什么用?

答案就是“省电”,当多核CPU没有任务执行的时候,这时候需要将除主Core之外的其他Core进行低功耗处理,这件事就是CPU Idle机制做的。

idle状态: 在Linux kernel中,当cpu中没有任务在执行,也没有任何中断、异常信号过来的时候,我们称为处于idle状态,针对这种状态Linux设计了一套cpuidle framework框架,专门用于cpuidle的管理。

Linux系统初始化时会为每个cpu创建一个idle线程,当没有其他进程需要运行的时候,便运行idle线程。

对于不同的功耗及恢复时间的要求,可以根据芯片硬件支持的情况定义多种idle状态,这些状态按功耗从低到高(对应着恢复时间从少到多)排列,利用linux提供的cpuidle框架,用户选用不同的idle策略。这么做的目的就是尽可能在不影响性能的前提下,减少功耗。

在ARM64架构中,至少会提供一个wfi的idle状态,有些芯片可能还会提供core下电的idle状态。当CPU idle时,根据预测的idle时间、功耗受益大小、恢复的时间长短,选用一个idle状态,比如进入wfi,关掉CPU的arch timer以便降低功耗,当有中断触发时,CPU又会恢复回来。

2. CPU Idle整体框架

调度器

首先是CPUIdle子系统通过sysfs向userspace提供的节点:

一类是针对整个系统的/sys/devices/system/cpu/cpuidle,通过其中的current_driver、current_governor、available_governors等节点可以获取或设置CPUIdle的驱动信息以及governor。

一类是针对每个CPU的/sys/devices/system/cpu/cpux/cpuidle,通过子节点暴露各个在线的CPU中每个不同Idle级别的name、desc、power、latency等信息。

什么时候进入idle?

关闭一些核可以节省功耗,但关闭之后对时延(性能)必会造成一定的影响,如果在关闭之后很短的时间内就被唤醒,那么就会造成功耗/性能双方都不讨好,在进入退出idle的过程中也是会有功耗的损失的,如果在idle状态下面节省的功耗还无法弥补进入退出该idle的功耗,那么反而会得不偿失。

--解决方法就是:策略。是由cpuidle framework会根据不同的场景来进行仲裁选择使用何种的idle状态。

调度器

在kernel中cpuidle framework主体包含三个模块,分别为cpuidle core、cpuidle governors和cpuidle drivers,

cpu idle core:负责整体框架,同时负责和sched模块对接,当调度器发现没有任务在执行时候,就切换到idle进程,通知到cpuidle framework的cpuidle core模块要做接下来的idle操作。向cpuidle driver/governors模块提供统一的driver和governors注册和管理接口,向用户空间程序提供governor选择的接口。

cpuidle driver:负责具体idle机制的实现(不同等级下面idle的指标也是在这个模块进行填充),不同的平台会有不同的drivers实现。

cpudile governors:在这个模块进行cpuidle的选择,选择的算法主要是基于切换的功耗代价和系统的延迟容忍度,电源管理的目标就是在保证延迟在系统可以接受的范围内尽可能的节省功耗。

3. Idle状态判断

在Linux系统启动的时候,会在每个cpu上创建对应的idle进程,start_kernel()函数初始化内核需要的所有数据结构,并创建一个名为init的进程(pid=1),当init进程创建完后,cpu的idle进程处于cpu_idle_loop()无限循环中,当没有其他进程处于TASK_RUNNING状态时候,调度器才会执行cpu idle线程,让cpu进入idle模式.其函数调用关系简要概括如下:

start_kernel –> rest_init –> cpu_startup_entry, 在cpu_startup_entry这个函数中,最终程序会进入无限循环do_idle loop中。

这里我们又进入看代码环节,可以参考公众号之前的文章# Linux驱动-IMX6ULL开发板qemu环境搭建,我们修改好qemu启动脚本加-s -S后,在VS中打断点,进行代码查看。

调度器

cpu_idle_loop()函数中会不断的进行轮询判断

 

while (1) {
    ...
    if (cpu_is_offline(cpu)) {
            cpuhp_report_idle_dead();
            arch_cpu_idle_dead();
    }

    local_irq_disable();
    arch_cpu_idle_enter();
    ...
}

 

3. cpuidle core

cpuidle core抽象出了三个数据结构:

cpuidle device:用于描述CPU核的cpuidle设备。

cpuidle driver:用于描述CPU核的cpuidle驱动。

cpuidle governor:主要根据cpuidle的device和driver状态来选择策略。

调度器

以cpuidle-pcsi.c为例,整个cpuidle注册流程如下图:

调度器

4. 注册初始化

cpuidle初始化包括governor注册、驱动注册和设备注册三部分

4.1 cpuidle governor注册

调度器

cpuidle governor在cpuidle驱动和设备之前注册,内核使用一个链表维护系统中所有已注册的governor。当前新版内核一共支持ladder、menu、teo和haltpoll四种governor,它们都通过调用cpuidle_register_governor函数将自身注册到系统中。

Haltpoll governor:它是用于优化虚拟机性能的一种cpuidle governor。其原理为当vcpu进入idle时,通过guest端执行poll操作,以避免使其陷入host中。它的优点是减少了vm切换和通过ipi唤醒vcpu的成本,但它也造成在guest睡眠时,host无法复用该vcpu对应的物理cpu,从而降低系统吞吐量的问题。

Ladder governor:该governor通过cpu前一次idle状态的驻留时间是否超过该state延迟时间一个特定的值(promotion_time_ns),以及下一个state的延迟时间是否超过系统延迟容忍度,来确定是否需要提升idle state。由于该governor每次只能提升一个state,因此其state提升方式就像梯子一样逐级往上,这也是它的名字由来。它往往用于periodic timer tick system。

Menu governor:直接选择可能满足需求的最深休眠态,就好像你拿着菜单(menu)选菜一样。如果深度的idle state更好,那么就会直接进入到深度的idle state。

Teo governor:采用的策略跟menu governor一样,都是预测接下来会有多长时间能待在idle状态,然后据此选择合适的idle mode。不过它跟menu governor考虑多方因素的策略是不同的。teo的理念是,多数系统上CPU唤醒最频繁的唤醒源都是timer events,而不是设备中断(device interrupts)。timer中断的数量要比其他中断高几个数量级。所以只要依据timer event就可以做好预测工作了。

4.2 cpuidle driver注册

调度器

cpuidle驱动注册流程比较简单,它主要包含以下三部分内容

idle state相关参数设置、以及可能的broadcast timer

若设置了local-timer-stop属性,则为每个cpu设置相应的broadcast timer

若为该driver指定了governor,则切换current governor

cpuidle driver的主要工作是定义所支持的cpuidle state,以及state的enter接口,如下面所示,cpudile driver就要负责将平台定义的idle-state信息填充到这个结构体中

调度器

4.3 cpuidle device注册

调度器

cpuidle设备注册主要包括初始化一些参数值,将该设备添加到全局设备链表中,然后为其初始化sysfs属性和使能该设备。

注册之后,cpuidle设备、cpuidle驱动及governor之间建立起了连接,最终系统经由cpuidle framework,通过接口来调用下层的接口,进而完成具体的硬件操作。

在现在的SMP系统中,每个cpu core都会有一个对应的cpuidle device,内核是通过使用struct cpuidle_device抽象cpuidle device,该结构体主要成员含义如下:

lenabled:设备是否已经使能 lcpu:该device对应的cpu number llast_residency:该设备上一次停留在idle状态的时间 lstates_usage:记录了该设备的每个idle state的统计信息

5. cpuidle触发流程

Idle task通过cpu_startup_entry为入口,调用到cpuidle_framework,流程如下图:

调度器

cpu启动完成时,会通过cpu_startup_entry函数将其自身切换到idle线程。除此之外,当某个cpu上没有可运行线程时,也会切换idle线程(上流程没画出,后面梳理进程调度的时候再细讲)。切换idle线程后,最终都会执行idle线程的主函数do_idle,并最终通过该函数将cpu设置为特定的idle state。

其中governor中的select、reflect函数是cpuidle的核心功能,决定了cpuidle状态的选择策略。








审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分