TPIDR_EL1(Thread Process ID Register for EL1)是 ARM 架构中一个特殊的寄存器,用于存储当前执行线程或进程的上下文信息。在操作系统内核中,这个寄存器经常被用来存储指向per_cpu_data结构的指针,该结构包含了特定于 CPU 的数据,比如 CPU 的局部变量和栈指针
/* * @boot_flag is boot flag addresses for smp; * @info is now only used as board_revision for rpi4. */ voidmain(paddr_t boot_flag, void *info) { // ... /* Other cores are busy looping on the boot_flag, wake up those cores */ enable_smp_cores(boot_flag);
// ...
smc_init();
// ...
/* Create initial thread here, which use the `init.bin` */ create_root_thread(); kinfo("[ChCore] create initial thread done\n");
/* Leave the scheduler to do its job */ sched();
// ... }
多核启动
初始调度
第一个问题来了:在创建第一个线程时,所有内核均已启动,而这时候并没有等待的别线程,那调度给谁呢?
答案是自己调度给自己,并且会有 idle 优化(空闲线程优化),这部分内容在 Linux 中亦有记载:
if (!list_empty(&(rr_ready_queue_meta[cpuid].queue_head))) { lock(&(rr_ready_queue_meta[cpuid].queue_lock)); again: if (list_empty(&(rr_ready_queue_meta[cpuid].queue_head))) { unlock(&(rr_ready_queue_meta[cpuid].queue_lock)); goto out; } /* * When the thread is just moved from another cpu and * the kernel stack is used by the origina core, try * to find another thread. */ if (!(thread = find_runnable_thread( &(rr_ready_queue_meta[cpuid].queue_head)))) { unlock(&(rr_ready_queue_meta[cpuid].queue_lock)); goto out; }
BUG_ON(__rr_sched_dequeue(thread)); if (thread_is_exiting(thread) || thread_is_exited(thread)) { /* Thread need to exit. Set the state to TE_EXITED */ thread_set_exited(thread); goto again; } unlock(&(rr_ready_queue_meta[cpuid].queue_lock)); return thread; } out: return &idle_threads[cpuid]; }
注意到在等待队列为空的时候,会来到标签 out ,返回一个 idle_thread ,即空闲线程
它的 ctx 会在初始化的时候被放在 idle_thread_routine 处,这个函数是体系结构相关的,旨在防止 cpu 空转降低功耗
1 2
/* Arch-dependent func declaraions, which are defined in assembly files */ externvoididle_thread_routine(void);
进一步阅读汇编代码,这个函数在 arm 架构中是 wfi 指令,让 cpu 进入低功耗状态,在某些版本中的实现是关闭几乎所有的时钟
1 2 3 4
BEGIN_FUNC(idle_thread_routine) idle: wfi b idle END_FUNC(idle_thread_routine)
// kernel/arch/aarch64/boot/raspi3/init/init_c.c /* * Initialize these varibles in order to make them not in .bss section. * So, they will have concrete initial value even on real machine. * * Non-primary CPUs will spin until they see the secondary_boot_flag becomes * non-zero which is set in kernel (see enable_smp_cores). * * The secondary_boot_flag is initilized as {NOT_BSS, 0, 0, ...}. */ #define NOT_BSS (0xBEEFUL) long secondary_boot_flag[PLAT_CPU_NUMBER] = {NOT_BSS}; // 0xBEEFUL volatile u64 clear_bss_flag = NOT_BSS;
/* * @boot_flag is boot flag addresses for smp; * @info is now only used as board_revision for rpi4. */ voidmain(paddr_t boot_flag, void *info) { // ...
/* Other cores are busy looping on the boot_flag, wake up those cores */ enable_smp_cores(boot_flag);
// ...
/* Create initial thread here, which use the `init.bin` */ create_root_thread();
/* Leave the scheduler to do its job */ sched();
// ... }
从 main 函数的签名以及 enable_smp_cores 函数的实现也可以看出来,我们需要先进行一次转换得到虚拟地址,再进行后续的操作: