[Linux]学习笔记系列 -- [drivers][base]cpu


title: cpu

categories:

  • linux
  • drivers
  • base
    tags:
  • linux
  • drivers
  • base
    abbrlink: ba80502e
    date: 2025-10-03 09:01:49

https://github.com/wdfk-prog/linux-study

文章目录

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的生命周期。

  1. CPU设备抽象 :内核启动时,会为每个"可能存在"的CPU在sysfs文件系统下创建对应的设备目录,通常位于 /sys/devices/system/cpu/cpuX
  2. 用户空间触发 :管理员或自动化脚本通过向 /sys/devices/system/cpu/cpuX/online 文件写入 0(离线)或 1(上线)来发起热插拔请求。
  3. 热插拔线程 :为了避免在中断或关键上下文中执行耗时的热插拔操作,系统为每个CPU都创建了一个名为 cpuhp/X 的内核线程,专门用于执行该CPU的上线或离线回调任务。
  4. 状态迁移与回调
    • 离线过程:当一个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();
}
相关推荐
zhaoyun9271 小时前
ubuntu linux 安装net8 net9 net 10方法
linux·ubuntu
大母猴啃编程1 小时前
线程同步与互斥
linux
九皇叔叔2 小时前
使用 perf + FlameGraph 生成火焰图(Flame Graph)笔记
笔记·性能分析·火焰图
Arms2062 小时前
python时区库学习
开发语言·python·学习
世人万千丶2 小时前
Day 5: Flutter 框架文件系统交互 - 鸿蒙沙盒机制下的文件读写与安全策略
学习·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
℡終嚸♂6802 小时前
渗透测试前四天笔记
笔记
zwtahql2 小时前
ubuntu远程ssh连接
linux·ubuntu·ssh
子云之风2 小时前
LSPosed 项目编译问题解决方案
java·开发语言·python·学习·android studio
南烟斋..2 小时前
嵌入式系统(51单片机)核心外设详解:UART通信与DS18B20温度采集
linux·运维·网络