CPU管理位图

465 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

内核对CPU的管理是通过位图(bitmap)变量来管理的,并且定义了possible、present、online和active这4种状态。


<include/linux/cpumask.h>

#define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
#define cpu_online_mask   ((const struct cpumask *)&__cpu_online_mask)
#define cpu_present_mask  ((const struct cpumask *)&__cpu_present_mask)
#define cpu_active_mask   ((const struct cpumask *)&__cpu_active_mask)

  • cpu_possible_mask:表示系统中有多少个可以运行(现在运行或者将来某个时间点运行)的CPU内核。
  • cpu_online_mask:表示系统中有多少个正处于运行(online)状态的CPU内核。
  • cpu_present_mask:表示系统中有多少个可处于运行状态的CPU内核,它们不一定都处于运行状态,有的CPU内核可能被热插拔了。
  • cpu_active_mask:表示系统中有多少个活跃的CPU内核。

上述4个变量都是位图类型变量。 位图类型变量使用一个长整数类型数组name[],每位代表一个CPU。对于64位处理器来说,一个长整数类型数组成员只能表示64个CPU内核。内核配置中有一个宏CONFIG_NR_CPUS,表示该系统最大的CPU内核数量。假设CONFIG_NR_CPUS为8,那么只需要一个长整数类型数组成员即可。cpumask数据结构本质上也是位图,内核通常使用cpumask的相关接口函数来管理CPU内核数量,lib/cpumask.c和include/linux/cpumask.h文件中实现了大部分与cpumask相关的接口函数。


#define DECLARE_BITMAP(name,bits) \
    unsigned long name[BITS_TO_LONGS(bits)]

typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

下面先看cpu_possible_mask的初始化。


<start_kernel()->setup_arch()->smp_init_cpus()>

void __init smp_init_cpus(void)
{
    int i;

    if (acpi_disabled)
        of_parse_and_init_cpus();
    else
        acpi_parse_and_init_cpus();

    for (i = 1; i < nr_cpu_ids; i++) {

        if (cpu_logical_map(i) != INVALID_HWID) {
            if (smp_cpu_setup(i))
                cpu_logical_map(i) = INVALID_HWID;
        }
    }
}

在系统启动时,smp_init_cpus()函数会通过ACPI或者DTS表来查询和获取CPU内核的数量,然后通过smp_cpu_setup()函数设置到cpu_possible_bits位图中,从而设置cpu_possible_mask变量。 of_parse_and_init_cpus()函数用于查询DTS表,而acpi_parse_and_init_cpus()函数用于查询ACPI表。 smp_cpu_setup()函数调用set_cpu_possible()函数来设置cpu_possible_mask位图。

static inline void
set_cpu_possible(unsigned int cpu, bool possible)
{
    if (possible)
        cpumask_set_cpu(cpu, &__cpu_possible_mask);
    else
        cpumask_clear_cpu(cpu, &__cpu_possible_mask);
}


知道了cpu_possible_mask位图,下面看一下cpu_present_mask位图是怎么得到的。

<start_kernel()->rest_init()->kernel_init()->kernel_init_freeable()->smp_prepare_
cpus()>

void __init smp_prepare_cpus(unsigned int max_cpus)
{
    for_each_possible_cpu(cpu) {
        set_cpu_present(cpu, true);
    }
}

在系统初始化SMP时,smp_prepare_cpus()函数把cpu_possible_mask复制到cpu_present_mask中。 下面看一下cpu_active_mask是如何初始化的。

<start_kernel()->rest_init()->kernel_init()->kernel_init_freeable()->smp_init()>

void __init smp_init(void)
{
    pr_info("Bringing up secondary CPUs ...\n");

    for_each_present_cpu(cpu) {
            cpu_up(cpu);
    }
}

smp_init()函数遍历cpu_present_mask中的CPU内核,然后使能该CPU内核。该CPU内核使能完成(见cpu_up()函数)后就会被添加到cpu_active_mask变量中,总结如下。

  • cpu_possible_mask通过查询系统DTS表或者ACPI表获取系统CPU内核数量。
  • cpu_present_mask等同于cpu_possible_mask。
  • cpu_active_mask是经过使能后(见cpu_online()函数)的CPU内核数量。