title: cpu
categories:
- linux
- drivers
- base
tags: - linux
- drivers
- base
abbrlink: ba80502e
date: 2025-10-03 09:01:49

文章目录
-
- [drivers/base/cpu.c CPU设备管理(CPU Device Management) CPU热插拔的核心实现](#drivers/base/cpu.c CPU设备管理(CPU Device Management) CPU热插拔的核心实现)
- [register_cpu: 注册一个CPU设备并创建其sysfs接口](#register_cpu: 注册一个CPU设备并创建其sysfs接口)
- [arch_register_cpu: 注册一个CPU设备(架构相关的弱实现)](#arch_register_cpu: 注册一个CPU设备(架构相关的弱实现))
- [cpu_dev_register_generic: 注册所有存在的CPU设备](#cpu_dev_register_generic: 注册所有存在的CPU设备)
- [cpu_dev_init: 初始化CPU子系统并创建sysfs接口](#cpu_dev_init: 初始化CPU子系统并创建sysfs接口)
drivers/base/cpu.c CPU设备管理(CPU Device Management) CPU热插拔的核心实现
drivers/base/cpu.c 是Linux内核设备模型中的一个基础文件,其核心职责是将系统中的各个CPU(中央处理器)抽象为标准的设备,并通过sysfs文件系统进行管理。该文件最主要的功能是为Linux内核提供CPU热插拔(CPU Hotplug)机制的通用框架,允许在系统运行时动态地使CPU核心上线(online)或离线(offline)。
历史与背景
这项技术是为了解决什么特定问题而诞生的?
CPU热插拔机制的出现主要是为了满足日益复杂的系统需求:
- RAS(可靠性、可用性、可服务性):在高端服务器和NUMA(非统一内存访问)硬件中,系统需要能够在不宕机的情况下,物理上移除发生故障的CPU节点,或添加新的计算资源。
- 电源管理:在负载较低时,可以将闲置的CPU核心完全离线,从而彻底切断其电源,实现比普通待机模式更深层次的节能。
- 资源灵活性与虚拟化:在虚拟化环境中,可以根据虚拟机的负载动态增加或减少vCPU(虚拟CPU)的数量,实现计算资源的弹性伸缩,优化资源利用率。
- 系统启动与休眠 :在多核(SMP)系统中,CPU热插拔的机制也被用于简化系统的启动(例如,通过
maxcpus内核参数限制启动时在线的CPU数量)和挂起/恢复流程。
它的发展经历了哪些重要的里程碑或版本迭代?
CPU热插拔功能并非Linux内核与生俱来。早期内核中的所有硬件均在启动时一次性完成检测和初始化。随着模块化机制的引入,Linux开始具备运行时加载/卸载驱动的能力,为更复杂的硬件热插拔奠定了基础。 针对CPU的热插拔最初是为特定高端服务器硬件开发的补丁,后来逐渐演变为一个通用的内核子系统。 其发展过程中的一个重要演进是从简单的CPU上下线操作,发展为一个精细、分阶段的状态机,允许内核的其他子系统(如调度器、定时器、中断等)在CPU状态变化的特定节点注册回调函数,从而确保整个系统在CPU数量动态变化时保持稳定和一致。
目前该技术的社区活跃度和主流应用情况如何?
CPU热插拔是一项成熟且仍在持续维护的内核核心技术。它被广泛应用于:
- 云计算和虚拟化:各大云服务商的核心功能之一,用于实现虚拟机的弹性计算。
- 数据中心:用于物理服务器的故障隔离、维护和动态资源配置。
- 移动和嵌入式设备:通过在低负载时关闭部分CPU核心来显著延长电池续航。
- 高性能计算(HPC):根据计算任务的并行度需求,动态调整参与计算的CPU核心数量。
核心原理与设计
它的核心工作原理是什么?
drivers/base/cpu.c 的核心原理是围绕一个严格的状态机来管理CPU的生命周期。
- CPU设备抽象 :内核启动时,会为每个"可能存在"的CPU在sysfs文件系统下创建对应的设备目录,通常位于
/sys/devices/system/cpu/cpuX。 - 用户空间触发 :管理员或自动化脚本通过向
/sys/devices/system/cpu/cpuX/online文件写入0(离线)或1(上线)来发起热插拔请求。 - 热插拔线程 :为了避免在中断或关键上下文中执行耗时的热插拔操作,系统为每个CPU都创建了一个名为
cpuhp/X的内核线程,专门用于执行该CPU的上线或离线回调任务。 - 状态迁移与回调 :
- 离线过程:当一个CPU被请求离线时,系统会启动一个多阶段的移除流程。首先,内核会将该CPU上的所有进程迁移到其他在线的CPU上。接着,绑定在该CPU上的中断、定时器等资源也会被迁移。 然后,系统会按照预定义的逆序调用一系列已注册的回调函数,通知内核各子系统(调度器、内存管理、驱动等)为该CPU的离线做准备。在所有准备工作完成后,该CPU最终进入深度休眠或断电状态。
- 上线过程 :过程与离线相反。目标CPU被唤醒,并顺序执行一系列启动回调函数,用于初始化其 per-CPU 数据结构和相关子系统状态。当所有回调成功执行后,该CPU被标记为
online状态,正式加入调度器的可用CPU池中。
它的主要优势体现在哪些方面?
- 灵活性:无需重启系统即可动态调整计算资源。
- 高节能性:将CPU核心完全下电是最高效的CPU节能方式。
- 可靠性:能够安全地隔离有故障的CPU,防止其影响整个系统的稳定性。
- 资源优化:在虚拟化场景中,通过动态调整vCPU,可以显著提高物理资源的利用率。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 性能开销:CPU上线和离线的过程涉及大量复杂操作,如任务迁移、缓存刷新和资源重新分配,会带来不可忽略的延迟和性能开销。
- 软件兼容性问题:并非所有软件都能很好地处理CPU数量的动态变化。一些设计不佳的应用程序或内核驱动可能会在CPU热插拔后出现功能错误或性能下降。
- 对vNUMA的负面影响:在虚拟化环境中(如VMware),为虚拟机启用vCPU热添加功能,在vSphere 8.0之前通常会自动禁用vNUMA(虚拟非统一内存访问)。 这会导致虚拟机操作系统失去对底层NUMA拓扑的感知,对于大型数据库等对内存访问延迟敏感的应用可能造成性能下降。
- 启动CPU的限制 :通常情况下,系统的引导CPU(通常是CPU0)不能被离线,因为它承担了一些特殊的系统初始化和管理任务。但在某些架构(如x86)上,可以通过特定的内核参数(如
cpu0_hotplug)来解除这一限制。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。
- 云主机弹性伸缩:当网站流量激增时,云平台可以自动为承载网站的虚拟机热添加vCPU以应对高负载;当流量回落时,再热移除多余的vCPU以节约成本。
- 服务器硬件维护:当监控系统检测到某个物理CPU核心出现大量硬件错误时,系统管理员可以将其热插拔离线,以防止错误扩散,同时保持服务器在线服务。
- 移动设备电源管理:智能手机在执行轻量级任务(如阅读)时,可以自动将高性能核心离线,只保留能效核心工作,从而最大化地延长电池续航。
是否有不推荐使用该技术的场景?为什么?
- 硬实时系统:CPU热插拔过程中的延迟是不可预测的,这会破坏硬实时系统所需的时间确定性,因此不推荐使用。
- 对NUMA拓扑高度敏感的应用:对于大型数据库、科学计算等需要精细优化NUMA局部性的应用,如果启用CPU热插拔会导致vNUMA被禁用,那么静态配置CPU资源通常是更好的选择,以避免跨NUMA节点的内存访问所带来的性能损失。
- 缺乏充分测试的复杂环境:在运行了大量未经CPU热插拔兼容性测试的应用程序的生产环境中,随意使用该功能可能会引入潜在的稳定性和性能风险。
对比分析
请将其 与 其他相似技术 进行详细对比。
在Linux中,与CPU资源和功耗管理相关的技术主要还有CPUIdle和CPU Frequency Scaling (cpufreq)。它们的目标相似,但机制和适用场景完全不同。
| 特性 | CPU Hotplug (CPU热插拔) | CPUIdle (CPU空闲状态) | CPU Frequency Scaling (cpufreq) |
|---|---|---|---|
| 功能概述 | 将CPU核心从操作系统中完全移除或加入,使其在线(online)或离线(offline)。 | 当CPU没有任务时,让其进入不同的低功耗睡眠状态(C-states)。 | 根据系统负载动态调整CPU的工作频率和电压(P-states)。 |
| 实现方式 | 由drivers/base/cpu.c主导的复杂状态机,涉及任务迁移和多级内核子系统回调。 |
由cpuidle框架和驱动管理,调度器在CPU空闲时调用。 |
由cpufreq框架、驱动和调速器(governor)策略共同决定。 |
| 性能开销 | 高。状态转换是重量级操作,耗时可达毫秒甚至秒级。 | 低。进出C-state的延迟通常在微秒级别,是轻量级操作。 | 中等。改变频率和电压的速度快于热插拔,但慢于进入Idle状态。 |
| 资源占用 | 内核中维护状态机和per-CPU线程,开销相对较大。 | 框架本身开销极小。 | 框架和调速器开销极小。 |
| 隔离级别 | 完全隔离。离线的CPU对调度器完全不可见,不参与任何计算。 | 不隔离。CPU仍在线,随时可以被中断或新任务唤醒。 | 不隔离。CPU持续在线并处理任务,只是运行在不同的性能水平上。 |
| 转换速度 | 慢 | 非常快 | 快 |
| 主要用途 | 长期的资源调整、故障隔离、最大化节能。 | 短期的、频繁的、机会性的节能。 | 在性能和功耗之间取得实时动态平衡。 |
register_cpu: 注册一个CPU设备并创建其sysfs接口
此函数是Linux内核中将一个CPU正式注册为"设备"的核心步骤。它接收一个描述CPU属性的struct cpu对象和一个CPU编号,然后初始化其中内嵌的struct device,并将其注册到CPU子系统(总线)中。这个注册动作的最终结果是在sysfs文件系统中创建出代表该CPU的设备目录(如/sys/devices/system/cpu/cpu0)以及其所有的属性文件。
c
/*
* register_cpu -为一个CPU设置一个sysfs设备.
* @cpu - cpu->hotpluggable 字段被设为1时, 会为此CPU在sysfs中生成一个控制文件.
* @num - 创建设备时使用的CPU编号.
*
* 初始化并注册CPU设备.
*/
int register_cpu(struct cpu *cpu, int num)
{
/*
* 定义一个整型变量 error, 用于存储函数调用的返回值.
*/
int error;
/*
* 设置CPU所属的NUMA节点ID. 对于非NUMA架构的STM32, cpu_to_node(0)会返回0.
*/
cpu->node_id = cpu_to_node(num);
/*
* 使用0x00字节串初始化cpu结构体中内嵌的device成员.
* 这是一个关键的步骤, 确保在填充字段前所有成员都处于已知的零状态.
*/
memset(&cpu->dev, 0x00, sizeof(struct device));
/*
* 设置设备的ID. 对于CPU 0, 这里设置为0.
* 总线类型会用这个ID和它的dev_name("cpu")来生成最终的设备名 "cpu0".
*/
cpu->dev.id = num;
/*
* 将此设备关联到cpu_subsys总线. 这告诉内核驱动核心如何管理这个设备.
*/
cpu->dev.bus = &cpu_subsys;
/*
* 设置release回调函数. 当此设备的最后一个引用被释放时, 内核会调用cpu_device_release
* 来清理资源. 对于永不释放的CPU设备, 这主要是为了模型的完整性和正确性.
*/
cpu->dev.release = cpu_device_release;
/*
* 设置 offline_disabled 标志. 因为STM32H750的CPU不支持热插拔(hotpluggable为false),
* !cpu->hotpluggable 为 true, 表示此设备禁止被下线.
*/
cpu->dev.offline_disabled = !cpu->hotpluggable;
/*
* 设置设备的初始离线状态. cpu_online(num) 检查CPU当前是否在线.
* 在启动期间, CPU 0肯定是在线的, 所以cpu_online(0)为true, !true为false.
* cpu->dev.offline被设为false, 准确反映其在线状态.
*/
cpu->dev.offline = !cpu_online(num);
/*
* 获取与此CPU编号关联的设备树节点. of_get_cpu_node() 会在设备树中查找
* /cpus/cpu@<num> 节点. 这是将物理硬件描述与内核设备对象关联的关键步骤.
*/
cpu->dev.of_node = of_get_cpu_node(num, NULL);
/*
* 默认情况下, 将设备的属性组指向通用CPU属性组(common_cpu_attr_groups).
*/
cpu->dev.groups = common_cpu_attr_groups;
/*
* 检查CPU是否支持热插拔. 对于STM32H750, 此条件为false.
*/
if (cpu->hotpluggable)
/*
* 如果支持热插拔, 则覆盖为支持热插拔的属性组, 其中会包含 'online' 等控制文件.
* 这段代码在STM32H750上不会被执行.
*/
cpu->dev.groups = hotplugable_cpu_attr_groups;
/*
* 调用 device_register(), 将这个完全配置好的device对象注册到内核驱动核心.
* 此调用执行后, /sys/devices/system/cpu/cpu0 目录及其属性文件将出现在sysfs中.
*/
error = device_register(&cpu->dev);
if (error) {
/*
* 如果注册失败, 调用 put_device() 来减少引用计数并触发清理, 然后返回错误.
*/
put_device(&cpu->dev);
return error;
}
/*
* 将新注册的device对象的指针保存到一个per-cpu数组中.
* 这为内核其他部分提供了一个快速访问特定CPU设备对象的途径.
*/
per_cpu(cpu_sys_devices, num) = &cpu->dev;
/*
* 在NUMA节点的sysfs目录下创建一个指向CPU设备的符号链接.
* 对于STM32, 这会创建 /sys/devices/system/node/node0/cpu0 -> ../../cpu/cpu0.
*/
register_cpu_under_node(num, cpu_to_node(num));
/*
* 为该设备暴露一个电源管理服务质量(PM QoS)的延迟限制接口.
* PM_QOS_RESUME_LATENCY_NO_CONSTRAINT表示此CPU默认没有特定的恢复延迟要求.
*/
dev_pm_qos_expose_latency_limit(&cpu->dev,
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
/*
* 调用 set_cpu_enabled, 更新调度器相关的CPU掩码,
* 正式通知调度器此CPU现在可用.
*/
set_cpu_enabled(num, true);
return 0;
}
arch_register_cpu: 注册一个CPU设备(架构相关的弱实现)
此函数为一个指定的CPU编号创建一个struct cpu设备对象,并调用核心的register_cpu函数将其注册到CPU子系统中。__weak属性表示这是一个可以被特定架构(如ARM、x86)提供的更强实现所覆盖的默认版本。
c
/*
* 函数 arch_register_cpu
* __weak 关键字表示这是一个弱符号. 如果链接时存在一个同名的非弱符号(强符号),
* 链接器将使用强符号的版本, 忽略此弱符号版本. 这是一种允许架构(arch)
* 覆盖通用默认实现的机制.
* @cpu: 将要被注册的CPU的逻辑编号.
* @return: 成功时返回0, 失败时返回负的错误码.
*/
int __weak arch_register_cpu(int cpu)
{
/*
* 定义一个指向 struct cpu 的指针 c.
* per_cpu 是一个宏, 用于访问指定CPU的per-cpu变量.
* 此行代码的作用是获取全局per-cpu数组 cpu_devices 中, 索引为'cpu'的那个元素,
* 并将其地址赋值给 c. 每个CPU在系统中都有一个对应的 struct cpu 实例来描述它.
*/
struct cpu *c = &per_cpu(cpu_devices, cpu);
/*
* 设置 c->hotpluggable 字段.
* 这个字段标识了该CPU是否支持热插拔 (即在系统运行时动态地添加或移除).
* 它调用 arch_cpu_is_hotpluggable() 函数来获取这个信息.
* 对于像STM32H750这样的嵌入式处理器, CPU是固定的, 此函数将返回false.
*/
c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
/*
* 调用核心的 register_cpu 函数, 将这个描述CPU的设备对象(c)注册到内核中.
* 这是实际执行注册操作, 创建sysfs条目等工作的函数.
*/
return register_cpu(c, cpu);
}
cpu_dev_register_generic: 注册所有存在的CPU设备
此函数是一个__init函数,在内核启动时被调用一次。它负责遍历所有被硬件识别出的CPU,并为每一个CPU调用arch_register_cpu来进行注册。
c
/*
* 函数 cpu_dev_register_generic
* static 关键字表示此函数仅在当前文件中可见.
* __init 关键字告诉编译器将此函数放入特殊的初始化代码段,
* 在内核启动完成后, 这段代码所占用的内存会被释放, 以节约内存.
* void 表示此函数无参数和返回值.
*/
static void __init cpu_dev_register_generic(void)
{
/*
* 定义两个整型变量. i 用作循环计数器(CPU索引), ret 用于保存函数调用的返回值.
*/
int i, ret;
/*
* IS_ENABLED 是一个宏, 用于在编译时检查一个内核配置选项(Kconfig)是否被启用.
* 如果 CONFIG_GENERIC_CPU_DEVICES 没有被启用, 则直接返回, 不执行任何操作.
* 这样做可以裁剪掉不需要的功能, 减小内核体积.
*/
if (!IS_ENABLED(CONFIG_GENERIC_CPU_DEVICES))
return;
/*
* for_each_present_cpu 是一个宏, 用于遍历所有当前"存在"(被物理检测到)的CPU.
* 在像STM32H750这样的单核系统上, 这个循环只会执行一次, 变量 i 的值将是 0.
*/
for_each_present_cpu(i) {
/*
* 对当前CPU(i), 调用架构相关的注册函数.
*/
ret = arch_register_cpu(i);
/*
* 检查注册操作的返回值. 如果 ret 不为0 (表示有错误发生),
* 并且错误不是 -EPROBE_DEFER (表示一个依赖项尚未就绪, 需要稍后重试),
* 那么就打印一条警告信息.
*/
if (ret && ret != -EPROBE_DEFER)
pr_warn("register_cpu %d failed (%d)\n", i, ret);
}
}
cpu_dev_init: 初始化CPU子系统并创建sysfs接口
此函数在内核启动的早期被调用,其核心作用是在sysfs文件系统中建立起代表CPU的目录结构。它注册了一个名为"cpu"的子系统(总线),并在/sys/devices/system/cpu/下创建了代表系统中每个CPU的设备节点(如cpu0),以及一些用于查询和控制CPU状态的全局属性文件。
c
#define _CPU_ATTR(name, map) \
{ __ATTR(name, 0444, show_cpus_attr, NULL), map }
/*
* 定义一个 cpu_attr 结构体数组. 这个数组中的每一项都代表一个 sysfs 属性文件,
* 这些文件显示了系统中不同状态的CPU掩码.
* _CPU_ATTR 是一个宏, 用于方便地定义一个属性, 它将属性名和对应的CPU掩码变量关联起来.
*/
static struct cpu_attr cpu_attrs[] = {
/*
* 定义 "online" 属性. 它关联到 __cpu_online_mask 变量, 该变量是一个位掩码,
* 表示当前哪些CPU处于在线(可调度任务)状态.
* 在单核系统中, 该掩码的值如果CPU在线则为1 (二进制...01).
*/
_CPU_ATTR(online, &__cpu_online_mask),
/*
* 定义 "possible" 属性. 关联到 __cpu_possible_mask, 表示系统启动时内核认为
* 可能存在的CPU. 这个掩码在系统启动后通常是固定的.
* 在单核系统中, 该值为1.
*/
_CPU_ATTR(possible, &__cpu_possible_mask),
/*
* 定义 "present" 属性. 关联到 __cpu_present_mask, 表示实际被硬件检测到的CPU.
* 在单核系统中, 该值为1.
*/
_CPU_ATTR(present, &__cpu_present_mask),
};
/*
* 定义一个属性指针数组, 它集合了将要被注册在 /sys/devices/system/cpu/ 目录下的
* 所有全局属性文件的指针.
*/
static struct attribute *cpu_root_attrs[] = {
/*
* 这是一个条件编译块. 仅当内核配置了 CONFIG_ARCH_CPU_PROBE_RELEASE 时,
* probe 和 release 这两个属性文件才会被包含进来. 它们用于动态地探测和释放CPU.
* 对于固定的单核MCU, 此配置通常被禁用.
*/
#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
&dev_attr_probe.attr,
&dev_attr_release.attr,
#endif
/*
* 将上面定义的 online, possible, present 三个属性添加到数组中.
*/
&cpu_attrs[0].attr.attr,
&cpu_attrs[1].attr.attr,
&cpu_attrs[2].attr.attr,
/*
* &dev_attr_kernel_max.attr: 代表内核支持的最大CPU索引号 (nr_cpu_ids - 1).
* &dev_attr_offline.attr: 用于手动下线CPU的接口 (需要CONFIG_HOTPLUG_CPU).
* &dev_attr_enabled.attr: 打印可用(enabled)的CPU, 用于任务分配.
* &dev_attr_isolated.attr: 用于内核隔离(isocpus)功能的CPU.
*/
&dev_attr_kernel_max.attr,
&dev_attr_offline.attr,
&dev_attr_enabled.attr,
&dev_attr_isolated.attr,
/*
* 仅当配置了 CONFIG_NO_HZ_FULL (用于无动态时钟节拍的全速CPU)时, 才包含此属性.
*/
#ifdef CONFIG_NO_HZ_FULL
&dev_attr_nohz_full.attr,
#endif
/*
* 仅当配置了 CONFIG_CRASH_HOTPLUG (用于在系统崩溃时保留CPU状态)时, 才包含此属性.
*/
#ifdef CONFIG_CRASH_HOTPLUG
&dev_attr_crash_hotplug.attr,
#endif
/*
* 仅当配置了 CONFIG_GENERIC_CPU_AUTOPROBE (通用CPU自动探测)时, 才包含modalias属性,
* 用于CPU驱动的自动加载.
*/
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr,
#endif
/*
* 数组以NULL指针结尾, 这是内核属性数组的标准格式.
*/
NULL
};
/*
* 将属性数组封装进一个属性组结构体中.
*/
static const struct attribute_group cpu_root_attr_group = {
.attrs = cpu_root_attrs,
};
/*
* 创建一个属性组指针数组. 这样做是为了将来可以方便地添加更多的属性组.
*/
static const struct attribute_group *cpu_root_attr_groups[] = {
&cpu_root_attr_group,
NULL,
};
/*
* 这是CPU子系统总线类型的核心定义. 它描述了 "cpu" 总线的所有行为.
*/
const struct bus_type cpu_subsys = {
/*
* .name = "cpu": 总线的名称. 这将导致在sysfs中创建 /sys/bus/cpu 目录.
*/
.name = "cpu",
/*
* .dev_name = "cpu": 作为设备名称的前缀. 注册的CPU设备会被命名为 cpu0, cpu1 等.
*/
.dev_name = "cpu",
/*
* .match: 指向一个匹配函数(cpu_subsys_match). 当有新的CPU设备或驱动注册时,
* 内核会调用此函数来判断它们是否匹配.
*/
.match = cpu_subsys_match,
/*
* 这是一个条件编译块. 这些回调函数仅在内核配置了CPU热插拔(CONFIG_HOTPLUG_CPU)时才存在.
* 对于像STM32H750这样的固定单核系统, 此配置被禁用, 这两个字段将不存在于最终的结构体中.
*/
#ifdef CONFIG_HOTPLUG_CPU
/*
* .online: CPU上线操作的回调函数.
*/
.online = cpu_subsys_online,
/*
* .offline: CPU下线操作的回调函数.
*/
.offline = cpu_subsys_offline,
#endif
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
/*
* .uevent: 用于生成uevent事件的回调函数, 以便udev等用户空间程序可以自动加载CPU驱动.
*/
.uevent = cpu_uevent,
#endif
};
/*
* 将 cpu_subsys 的符号导出, 以便其他GPL许可证的内核模块可以引用它.
*/
EXPORT_SYMBOL_GPL(cpu_subsys);
/*
* 定义一个内联函数 cpu_register_vulnerabilities.
* 对于不涉及相关安全漏洞的简单CPU架构(如ARMv7-M), 此函数被定义为一个空函数体,
* 编译器会将其优化掉, 不产生任何开销.
*/
static inline void cpu_register_vulnerabilities(void) { }
/*
* 函数 cpu_dev_init
* __init 标记表示这是一个内核初始化函数. 在内核启动完成后,
* 该函数所占用的内存会被释放, 这对于内存有限的嵌入式系统至关重要.
*/
void __init cpu_dev_init(void)
{
/*
* 调用 subsys_system_register() 来注册CPU子系统.
* 这个函数会在 /sys/devices/system/ 目录下创建 "cpu" 目录,
* 并将 cpu_root_attr_groups 中定义的全局属性文件创建在其中.
* 同时, 它也会注册 cpu_subsys 总线类型, 创建 /sys/bus/cpu 目录.
*/
if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
/*
* 如果注册失败, 这是一个致命错误, 因为CPU是系统的核心.
* 调用 panic() 函数会使内核停止运行并打印错误信息.
*/
panic("Failed to register CPU subsystem");
/*
* 调用 cpu_dev_register_generic() 函数, 它会遍历系统中的所有CPU
* (在单核系统中只有一个), 并为每个CPU创建一个对应的设备 (即 /sys/devices/system/cpu/cpu0).
*/
cpu_dev_register_generic();
/*
* 调用一个空函数, 用于注册CPU安全漏洞相关信息. 在这里无任何操作.
*/
cpu_register_vulnerabilities();
}