持续创作,加速成长!这是我参与「掘金日新计划 · 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内核数量。