内核在启动secondary cpu之前当然需要为其准备好执行环境,因为内核中cpu最终都将由调度器管理,故此时调度子系统应该要初始化完成。
同时cpu启动完成转交给调度器之前,并没有实际的业务进程,而我们知道内核中cpu在空闲时会执行idle进程。因此,在其启动之前需要为每个cpu初始化一个idle进程。
另外,由于将一个cpu通过热插拔方式移除后,再次启动该cpu的流程,与secondary cpu的启动流程是相同的,因此内核复用了cpu hotplug框架用于启动secondary cpu。
而内核为每个cpu都分配了一个独立的hotplug线程,用于执行本cpu相关的热插拔流程。为此,内核通过以下流程执行secondary cpu启动操作:
以下代码为每个非boot cpu分配一个idle进程
void __init idle_threads_init(void)
{
…
boot_cpu = smp_processor_id();
for_each_possible_cpu(cpu) { (1)
if (cpu != boot_cpu)
idle_init(cpu); (2)
}
}
(1)遍历系统中所有的possible cpu
(2)若该cpu为secondary cpu,则为其初始化一个idle进程
以下代码为每个cpu初始化一个hotplug线程
void __init cpuhp_threads_init(void)
{
BUG_ON(smpboot_register_percpu_thread(&cpuhp_threads));
kthread_unpark(this_cpu_read(cpuhp_state.thread));
}
其中线程的描述结构体定义如下:
static struct smp_hotplug_thread cpuhp_threads = {
.store = &cpuhp_state.thread, (1)
.create = &cpuhp_create, (2)
.thread_should_run = cpuhp_should_run, (3)
.thread_fn = cpuhp_thread_fun, (4)
.thread_comm = "cpuhp/%u", (5)
.selfparking = true, (6)
}
(1)用于保存cpu上的task struct指针
(2)线程创建时调用的回调
(3)该回调用于获取线程是否需要退出标志
(4)cpu hotplug主函数,执行实际的hotplug操作
(5)该线程的线程名
(6)用于设置线程创建完成后,是否将其设置为park状态
全部0条评论
快来发表一下你的评论吧 !