linux之调度管理(6)-SMP系统 CPU 定义和初始化

一、为什么需要管理cpu

  cpu作为计算机系统的核心,主要执行运算和控制功能。在程序员眼中,只需要为其提供合适的电源和时钟,它就可以从指定位置开始读取和执行指令。因此,在传统的单核系统中,操作系统并不需要对cpu做特殊的管理。

        由于cpu的执行速度与其运行频率相关,因此在摩尔定律的驱动下,起先英特尔等芯片产商主要通过提高单核主频的方式,来提高cpu性能。但是随着芯片频率的提高,其运行功耗也会成指数级上升,因此在频率提高到一定程度之后,继续提升的难度就变得越来越大。

  为此,芯片产商改变了设计思路,转而通过在同一个die上集成多个相同的cpu核心来实现性能提升,这就是我们平常所说的smp。由于系统启动时主要执行一些镜像加载,以及软硬件初始化相关的任务。而这些流程并没有并行执行的需求,因此smp系统通常都由一个主cpu执行启动流程。而其它cpu则会等到操作系统初始化完成之后才会启动。

  更进一步,虽然smp系统的性能很强,但实际上很多时候并不需要这么高的计算能力,如我们当前只是使用浏览网页或听音乐等功能,此时完全没必要使用多个工作在最高频率的cpu核。若不对其做相应的管理,这种cpu性能的浪费会造成系统功耗增加。特别是对于移动设备,可能会严重影响其续航能力。

  为此,需要对cpu的功耗做更精细的管理,如在负载较低时调低cpu的频率和电压,当cpu进入idle状态时关闭其时钟,甚至将某些cpu完全从系统中移除等。以上这些功能的实现,首先自然离不开硬件的支持,其次也需要软件为其提供相应的驱动程序和管理策略,这也是为什么内核需要提供cpu管理相关模块的原因。在这个系列中,我们将以aarch64架构为例,按以下顺序分别介绍这些模块的原理和实现机制:

(1)cpu参数的初始化流程

(2)smp cpu的启动流程

(3)cpu hotplug原理和实现

(4)cpu idle原理和实现

(5)cpu dvfs原理和实现

二、如何标识cpu id

                arm64在smp系统中会为每个处理器分配一个表示其affinity关系的寄存器mpidr,该寄存器标识了不同处理器之间的亲和度。如位于相同cluster的处理器亲和度更高,在它们之间迁移进程的代价比跨cluster之间的迁移更小,因而可以给调度器的负载均衡策略提供参考。以下为其寄存器定义:

                由于在系统中每个处理器都具有唯一的mpidr值,因此它又可以被用于标识cpu的硬件id。与其它设备一样,arm64架构通过dts来配置每个cpu的硬件id,其示例格式如下:

cpu0: cpu@0 {
			compatible = "arm,cortex-a53";
			device_type = "cpu";
			reg = <0x0 0x1>;
			enable-method = "psci";
			next-level-cache = <&CLUSTER0_L2>;
			clocks = <&stub_clock 0>;
			operating-points-v2 = <&cpu_opp_table>;
			cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP>;
			#cooling-cells = <2>; 
			dynamic-power-coefficient = <311>;
		};

          即在内核中cpu也被抽象成了一种设备,并且可以为其配置一系列的参数。其中reg即用于配置其硬件id,而其它参数将会在后面相关模块中逐步介绍。

由于硬件id的格式与不同实现相关,为了方便统一管理,内核引入了cpu的逻辑id,将逻辑id与硬件id绑定后,即可以使用逻辑id来引用相关cpu。

 cpu逻辑id的定义如下:源码路径:kernel/arch/arm64/kernel/setup.c

u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
 

其中数组下标为cpu的逻辑id,数组成员用于保存其硬件id。linux内核规定primary cpu的逻辑id为0,而secondary cpu可通过dts获取其硬件id。

由于启动时的运行cpu即为primary cpu,因此其硬件id可在启动流程中从mpidr寄存器中直接读出。以下为其初始化流程图:

 其中smp_setup_processor_id的实现如下:

//kernel/arch/arm64/kernel/setup.c

void __init smp_setup_processor_id(void)
{
        u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
        set_cpu_logical_map(0, mpidr);

        /*
         * clear __my_cpu_offset on boot CPU to avoid hang caused by
         * using percpu variable early, for example, lockdep will
         * access percpu variable inside lock_release
         */
        set_my_cpu_offset(0);
        pr_info("Booting Linux on physical CPU 0x%010lx [0x%08x]\n",
                (unsigned long)mpidr, read_cpuid_id());
}

(1)从mpidr寄存器读取primary cpu的硬件id

(2)将该硬件id保存到__cpu_logical_map数组的第一个成员中,从而将其逻辑cpu号设置为0

                由于secondary cpu在启动初期就已通过wfe睡眠,而并不会参与启动流程,因此必须要由primary cpu帮助其完全cpu id的设置工作。而其参数需要从dts对应的dtb文件中读取,以下为其流程图:

                其中of_parse_and_init_cpus会遍历dtb中所有的cpu节点,获取其reg值,然后将其转换为64位后设置到对应的__cpu_logical_map数组中。以下为其代码主要实现:

//kernel/arch/arm64/kernel/smp.c

void __init smp_init_cpus(void)
{
        int i;

        if (acpi_di
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值