电子说
之前介绍了电源的开机和关机重启,本小节开始介绍省电的技术,其中最暴力的省电方法就是直接拔核hotplug处理,就像需要10个人干活都要吃饭,但是现在活少了最节省的方法就是砍掉几个人,有点像裁员啊。
1. 省电技术概览
对于省电,我们短时间不使用设备的时候可以进行休眠唤醒,长时间不使用就直接关机了。在使用设备的时候可以按照当前需要的性能进行调频处理就是CPUFreq和DevFeq,当没重度使用或者只运行系统必须进程的时候可以进行CPU休闲(CPUIdle)、CPU热插拔(CPU Hotplug)、CPU隔离(Core Isolate)和动态PM(Runtime PM)。
CPUIdle指的是当某个CPU上没有进程可调度的时候可以暂时局部关掉这个CPU的电源,从而达到省电的目的,当再有进程需要执行的时候再恢复电源。
CPU Hotplug指的是我们可以把某个CPU热移除,然后系统就不会再往这个CPU上派任务了,这个CPU就可以放心地完全关闭电源了,当把这个CPU再热插入之后,就对这个CPU恢复供电,这个CPU就可以正常执行任务了。
CPU隔离指的是我们把某个CPU隔离开来,系统不再把它作为进程调度的目标,这样这个CPU就可以长久地进入Idle状态了,达到省电的目的。不过CPU隔离并不是专门的省电机制,我们把CPU隔离之后还可以通过set_affinity把进程专门迁移到这个CPU上,这个CPU还会继续运行。CPU隔离能达到一种介于CPUIdle和CPU热插拔之间的效果。
Runtime PM指的是设备的动态电源管理,系统中存在很多设备,但是并不是每种设备都在一直使用,比如相机可能在大部分时间都不会使用,所以我们可以在大部分时间把相机的电源关闭,在需用相机的时候,再给相机供电。
cpu hotplug和idle的区别?
hotplug是从硬件上拔掉核下电,idle只是从软件上进行处理,也就是说调度器在idle时只是不去调用但是核还是可见的,hotplug直接没这个核了,软件完全不可见。
省电管理可以达到省电的目的,但是也会降低系统的性能,包括响应延迟、带宽、吞吐量等。所以内核又提供了一个PM QoS框架,QoS是Quality Of Service(服务质量)。PM QoS框架一面向顾客提供接口,顾客可以通过这些接口对系统的性能提出要求,一面向各种省电机制下发要求,省电机制在省电的同时也要满足这些性能要求。PM QoS的顾客包括内核和进程:对于内核,PM QoS提供了接口函数可以直接调用;对于进程,PM QoS提供了一些设备文件可以让用户空间进行读写。PM QoS对某一项性能指标的要求叫做一个约束,约束分为系统级约束和设备级约束。系统级约束针对的是整个系统的性能要求,设备级约束针对的是某个设备的性能要求。
整体上电源管理也是策略和机制分离的,例如:
hotplug是一个机制,谁去用?可以用户App制定的策略、温控策略、系统suspend时需要等。
CPUFreq是策略和机制都包含的。
2. 热插拔代码介绍
cpu的状态包括:possible、present、online、active。
possible状态的cpu:可理解为存在这个CPU资源,但还没有纳入Kernel的管理范围。
present状态的cpu:表示已经被kernel接管。
online状态的cpu:表示可以被调度器使用。
active状态的cpu:表示可以被迁移migrate。
Linux内核在初始的时候,会创建虚拟总线cpu_subsys,每个cpu调用register_cpu注册时,都会将cpu设备挂在这个总线下。cpu的拔插是通过操作文件节点online实现的,具体拔插操作如下(以cpu1为例):
echo 0 > /sys/devices/system/cpu/cpu1/online //拔核操作 echo 1 > /sys/devices/system/cpu/cpu1/online //插核操作
为什么以cpu1为例?
Linux CPU热插拔,支持在系统启动后,关闭任意一个secondary cpu(在ARM架构中,CPU0为boot cpu,不能被关闭),并在需要时重新打开它。
当操作/sys/devices/system/cpu/cpu1/online文件的时候,会执行drivers/base/core.c中online_store()函数
static ssize_t online_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count ) { bool val; int ret; ret = strtobool(buf, &val); if (ret < 0) return ret; ret = lock_device_hotplug_sysfs(); if (ret) return ret; ret = val ? device_online(dev) : device_offline(dev); unlock_device_hotplug(); return ret < 0 ? ret : count; } static DEVICE_ATTR_RW(online);
这块有一个sysfs的知识点,就是DEVICE_ATTR_RW(online);声明了这个宏,就可以在文件系统里面为这个设备熟悉添加一个文件,当向这个文件写入字符串的时候就会调用拼接出来的online_store()函数,读这个文件的时候就会调用online_show()函数
#define __ATTR(_name, _mode, _show, _store) { .attr = {.name = __stringify(_name), .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, .show = _show, .store = _store, } #define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store) #define DEVICE_ATTR_RW(_name) struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
在online_store()函数中,拔核就执行device_offline(dev)函数
device_offline中dev->bus->offline(dev);
drivers/base/cpu.c中
struct bus_type cpu_subsys = { .name = "cpu", .dev_name = "cpu", .match = cpu_subsys_match, #ifdef CONFIG_HOTPLUG_CPU .online = cpu_subsys_online, .offline = cpu_subsys_offline, #endif };
cpu_device_down cpu_down cpu_down_maps_locked _cpu_down cpuhp_down_callbacks takedown_cpu
[CPUHP_TEARDOWN_CPU] = { .name = "cpu:teardown", .startup.single = NULL, .teardown.single = takedown_cpu, .cant_stop = true, },
do_idle状态机会调用
arch_cpu_idle_dead cpu_die cpu_die psci_cpu_die psci_ops.cpu_off psci_0_2_cpu_off
psci_0_2_cpu_off会调用__psci_cpu_off(PSCI_0_2_FN_CPU_OFF, state);最终发送smc指令给ATF,上面的cpu down流程汇总如下图:
cpu up流程:
具体代码自己加log,或者打断点看好些。
3. ATF中处理
之前在电源管理入门-1关机重启详解中介绍的PSCI协议部分,这里会发送smc指令到ATF。在ATF中同理,会处理这些PSCI协议,这里不详细介绍了。
后记
本篇文章尝试用markdown进行编写,图片用Midjourney生成,感觉效果还可以,之前每篇文章的排版很费时间。markdown可以只保留最小的一些格式,把注意力关注到文章内容本身,提高效率才能多写一些文章进行更新。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !