在lk阶段可以实现smp。即将其他cpu都wakeup起来,每个cpu上都可运行,也可是实现调动,这是真正的并行编程.
从start.s开始cpu0 和 其他cpu走的不同的flow。smp相关的code都用WITH_SMP 抱起来了
/* stay in supervisor and call into arm arch code to continue setup */
mov r0, r5
bl arm_secondary_entry
如果不是cpu0 就调动arm_secondary_entry
#if WITH_SMP
void arm_secondary_entry(uint asm_cpu_num)
{
uint cpu = arch_curr_cpu_num();
if (cpu != asm_cpu_num)
return;
lk_secondary_cpu_entry();
}
继续看lk_secondary_cpu_entry的实现
void lk_secondary_cpu_entry(void)
{
uint cpu = arch_curr_cpu_num();
if (cpu > secondary_bootstrap_thread_count) {
dprintf(CRITICAL, "Invalid secondary cpu num %d, SMP_MAX_CPUS %d, secondary_bootstrap_thread_count %d\n",
cpu, SMP_MAX_CPUS, secondary_bootstrap_thread_count);
return;
}
thread_secondary_cpu_init_early();
thread_resume(secondary_bootstrap_threads[cpu - 1]);
dprintf(SPEW, "entering scheduler on cpu %d\n", cpu);
thread_secondary_cpu_entry();
}
最重要的函数式thread_secondary_cpu_entry
void thread_secondary_cpu_entry(void)
{
uint cpu = arch_curr_cpu_num();
thread_t *t = get_current_thread();
t->priority = IDLE_PRIORITY;
mp_set_curr_cpu_active(true);
mp_set_cpu_idle(cpu);
/* enable interrupts and start the scheduler on this cpu */
arch_enable_ints();
thread_yield();
idle_thread_routine();
}
调用idle_thread_routine 进入dile
static void idle_thread_routine(void)
{
for (;;)
arch_idle();
}
在进入idle之前会通过thread_yield()来在这颗cpu上进行调动
上述flow除cpu0 外每个cpu都会跑一边
thread_resched -> get_top_thread 在get_top_thread会分配一个新的thread运行
static thread_t *get_top_thread(int cpu)
{
thread_t *newthread;
uint32_t local_run_queue_bitmap = run_queue_bitmap;
while (local_run_queue_bitmap) {
/* find the first (remaining) queue with a thread in it */
uint next_queue = sizeof(run_queue_bitmap) * 8 - 1 - __builtin_clz(local_run_queue_bitmap);
list_for_every_entry(&run_queue[next_queue], newthread, thread_t, queue_node) {
#if WITH_SMP
if (newthread->pinned_cpu < 0 || newthread->pinned_cpu == cpu)
#endif
{
list_delete(&newthread->queue_node);
if (list_is_empty(&run_queue[next_queue]))
run_queue_bitmap &= ~(1<<next_queue);
return newthread;
}
}
local_run_queue_bitmap &= ~(1<<next_queue);
}
/* no threads to run, select the idle thread for this cpu */
return idle_thread(cpu);
}
在这个函数中可以看到run_queue 只有一个,并没有每个cpu分配一个run_queue。因此thread是可以在不同cpu上切换运行的,例如通过一个thread上一次在cpu0上运行,下一次可能在cpu1上运行.
从start.s开始cpu0 和 其他cpu走的不同的flow。smp相关的code都用WITH_SMP 抱起来了
/* stay in supervisor and call into arm arch code to continue setup */
mov r0, r5
bl arm_secondary_entry
如果不是cpu0 就调动arm_secondary_entry
#if WITH_SMP
void arm_secondary_entry(uint asm_cpu_num)
{
uint cpu = arch_curr_cpu_num();
if (cpu != asm_cpu_num)
return;
lk_secondary_cpu_entry();
}
继续看lk_secondary_cpu_entry的实现
void lk_secondary_cpu_entry(void)
{
uint cpu = arch_curr_cpu_num();
if (cpu > secondary_bootstrap_thread_count) {
dprintf(CRITICAL, "Invalid secondary cpu num %d, SMP_MAX_CPUS %d, secondary_bootstrap_thread_count %d\n",
cpu, SMP_MAX_CPUS, secondary_bootstrap_thread_count);
return;
}
thread_secondary_cpu_init_early();
thread_resume(secondary_bootstrap_threads[cpu - 1]);
dprintf(SPEW, "entering scheduler on cpu %d\n", cpu);
thread_secondary_cpu_entry();
}
最重要的函数式thread_secondary_cpu_entry
void thread_secondary_cpu_entry(void)
{
uint cpu = arch_curr_cpu_num();
thread_t *t = get_current_thread();
t->priority = IDLE_PRIORITY;
mp_set_curr_cpu_active(true);
mp_set_cpu_idle(cpu);
/* enable interrupts and start the scheduler on this cpu */
arch_enable_ints();
thread_yield();
idle_thread_routine();
}
调用idle_thread_routine 进入dile
static void idle_thread_routine(void)
{
for (;;)
arch_idle();
}
在进入idle之前会通过thread_yield()来在这颗cpu上进行调动
上述flow除cpu0 外每个cpu都会跑一边
thread_resched -> get_top_thread 在get_top_thread会分配一个新的thread运行
static thread_t *get_top_thread(int cpu)
{
thread_t *newthread;
uint32_t local_run_queue_bitmap = run_queue_bitmap;
while (local_run_queue_bitmap) {
/* find the first (remaining) queue with a thread in it */
uint next_queue = sizeof(run_queue_bitmap) * 8 - 1 - __builtin_clz(local_run_queue_bitmap);
list_for_every_entry(&run_queue[next_queue], newthread, thread_t, queue_node) {
#if WITH_SMP
if (newthread->pinned_cpu < 0 || newthread->pinned_cpu == cpu)
#endif
{
list_delete(&newthread->queue_node);
if (list_is_empty(&run_queue[next_queue]))
run_queue_bitmap &= ~(1<<next_queue);
return newthread;
}
}
local_run_queue_bitmap &= ~(1<<next_queue);
}
/* no threads to run, select the idle thread for this cpu */
return idle_thread(cpu);
}
在这个函数中可以看到run_queue 只有一个,并没有每个cpu分配一个run_queue。因此thread是可以在不同cpu上切换运行的,例如通过一个thread上一次在cpu0上运行,下一次可能在cpu1上运行.