cpu核数和内存大小怎么搭配 cpu核数( 二 )


ops = get_cpu_ops(cpu);
if (ops->cpu_init(cpu))
return -ENODEV;
set_cpu_possible(cpu, true);
return 0;
}
这里先了解一下内核中存在的四种CPU的状态表示,分别是possible、online、present和active 。
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
#define DECLARE_BITMAP(name,bits) \\
unsigned long name[BITS_TO_LONGS(bits)]
extern struct cpumask __cpu_possible_mask;
extern struct cpumask __cpu_online_mask;
extern struct cpumask __cpu_present_mask;
extern struct cpumask __cpu_active_mask;
#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)
可以从smp_cpu_setup函数看出,如果一个CPU编号对应的struct cpu_operations被建立成功,并且其对应的init函数没有执行失败,则将该CPU标记为possible 。
struct cpu_operations {
const char*name;
int(*cpu_init)(unsigned int);
int(*cpu_prepare)(unsigned int);
int(*cpu_boot)(unsigned int);
void(*cpu_postboot)(void);
#ifdef CONFIG_HOTPLUG_CPU
bool(*cpu_can_disable)(unsigned int cpu);
int(*cpu_disable)(unsigned int cpu);
void(*cpu_die)(unsigned int cpu);
int(*cpu_kill)(unsigned int cpu);
#endif
#ifdef CONFIG_CPU_IDLE
int(*cpu_init_idle)(unsigned int);
int(*cpu_suspend)(unsigned long);
#endif
};cpu_init:读取提出一个逻辑CPU的激活方法的必要数据;cpu_prepare:测试是否可以启动给定CPU;cpu_boot:启动CPU;cpu_postboot:启动CPU后必要的清理工作 。
除了上述的几个成员函数,可以看到其他成员函数是分别和CPU热插拔和CPUIDLE相关的电源操作,kernel抽象出了struct cpu_operations这个结构体,将启动、热插拔、空闲态接口统一起来,交给各个体系结构自己完成 。
start_kernel –> arch_call_rest_init –> rest_init –> kernel_init –> kernel_init_freezable –> smp_prepare_cpus
接下来一直到smp_prepare_cpus函数,才到设置present位图的时候 。
以下是smp_prepare_cpus的部分代码:
void __init smp_prepare_cpus(unsigned int max_cpus)
{
const struct cpu_operations *ops;
int err;
unsigned int cpu;
unsigned int this_cpu;
……
for_each_possible_cpu(cpu) {
per_cpu(cpu_number, cpu) = cpu;
if (cpu == smp_processor_id())
continue;
ops = get_cpu_ops(cpu);
if (!ops)
continue;
err = ops->cpu_prepare(cpu);
if (err)
continue;
set_cpu_present(cpu, true);
numa_store_cpu_info(cpu);
}
}
前面看到,possible位图的设置依赖于cpu_operations结构体的初始化以及是否读取到正确的CPU信息;那这里present位图的设置则依赖于cpu_prepare的执行结果,也就是检测对应CPU是否可以启动 。
kernel_init_freezable –> smp_init –> bringup_nonboot_cpus 。
void bringup_nonboot_cpus(unsigned int setup_max_cpus)
{
unsigned int cpu;
for_each_present_cpu(cpu) {
if (num_online_cpus() >= setup_max_cpus)
break;
if (!cpu_online(cpu))
cpu_up(cpu, CPUHP_ONLINE);
}
}
cpu_up –> _cpu_up –> cpuhp_up_callbacks –> cpuhp_invoke_callback –> bringup_cpu –> __cpu_up –> boot_secondary
cpu_up,这里可以这么简单的理解,最开始系统中只有一个CPU,当代码执行到这里的时候,开始启动其他的CPU,其他CPU的启动过程,首先是需要复制主CPU的0号线程(即空闲线程),然后去执行热插拔相关的回调函数,一直执行到boot_secondary,在这里最终会执行到struct cpu_operations中的回调函数cpu_boot 。