两种代码的差异性(public无法启用多核)
xv6-public的代码我无法启用多核,即使修改了CPUS数量也无法使用。
下面第一张图便是public在CPUS=4下运行的情况。
第二张图为RISC-V的多核启动CPUS=3的情况。
在我修改public的Makefile中关于qemu的参数后(增添了-smp cores=$(CPUS)),依旧未成功。
关于startothers的解析
startothers的作用
在多处理器系统中,操作系统启动时通常只有一个处理器(称为bootstrap processor,BSP)是活跃的,它负责初始化系统,包括内核和硬件的基本设置。一旦BSP完成了基础的初始化工作,它就会通过startothers
函数来启动其他的处理器(称为application processors,APs)。
- 定位内核入口:
startothers
首先需要确定内核的入口点,这样每个处理器在启动时都可以从相同的位置开始执行。 - 为每个AP设置栈空间:由于每个处理器都需要自己的栈来执行函数调用和处理中断,
startothers
会为每个AP分配并设置栈空间。 - 发送启动信号:使用启动内核(Startup Inter-Processor Interrupt,SIPI)通过APIC(Advanced Programmable Interrupt Controller)向每个AP发送信号,告诉它们开始执行。
- 等待APs启动:
startothers
可能会等待所有APs确认它们已经启动并且准备好接收调度任务。
public中的startothers
使用grep在public的代码中查找startothers(下面发现这个函数的定义在main.c中)
❯ grep -rnw '.' -e 'startothers'
grep: ./main.o: binary file matches
./entryother.asm:87: # Switch to the stack allocated by startothers()
./kernel.asm:6071: xchg(&(mycpu()->started), 1); // tell startothers() we're up
./kernel.asm:6212: kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
grep: ./xv6.img: binary file matches
./main.c:9:static void startothers(void);
./main.c:34: startothers(); // start other processors
./main.c:35: kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
./main.c:56: xchg(&(mycpu()->started), 1); // tell startothers() we're up
./main.c:64:startothers(void)
grep: ./kernel: binary file matches
./entryother.S:70: # Switch to the stack allocated by startothers()
startothers的代码解析
// Start the non-boot (AP) processors.
static void
startothers(void)
{
extern uchar _binary_entryother_start[], _binary_entryother_size[];
uchar *code;
struct cpu *c;
char *stack;
// Write entry code to unused memory at 0x7000.
// The linker has placed the image of entryother.S in
// _binary_entryother_start.
code = P2V(0x7000);
memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);
for(c = cpus; c < cpus+ncpu; c++){
if(c == mycpu()) // We've started already.
continue;
// Tell entryother.S what stack to use, where to enter, and what
// pgdir to use. We cannot use kpgdir yet, because the AP processor
// is running in low memory, so we use entrypgdir for the APs too.
stack = kalloc();
*(void**)(code-4) = stack + KSTACKSIZE;
*(void(**)(void))(code-8) = mpenter;
*(int**)(code-12) = (void *) V2P(entrypgdir);
lapicstartap(c->apicid, V2P(code));
// wait for cpu to finish mpmain()
while(c->started == 0)
;
}
}
疑惑:why do we need to use entrypgdir for APs?(上文蓝色段)
entryother.S中的关于startothers部分(完成启动过程中的初始化)
# Switch to the stack allocated by startothers()
movl (start-4), %esp
# Call mpenter()
call *(start-8)
startothers函数:这是在操作系统的主C代码(main.c)中定义的一个函数,负责逐个发送STARTUP IPI给每个AP,并且将特定的启动代码复制到内存地址0x7000处。此外,它还在启动代码的前几个字节处设置了一些关键的地址信息:
- start-4:新分配的每核栈的地址。
- start-8:跳转目标地址(mpenter函数的地址)。
- start-12:入口页目录(entrypgdir)的物理地址。
PS: 0x7000
用于存放操作系统在初始化多处理器系统时用到的特定代码
riscv中的多核启动
xv6-riscv的代码无法追踪到多核的启动函数,类似于public中的startothers。
说实话并不知道具体多核是怎样启动的,但是使用gdb调试可以发现,在运行sheduler之前多核并未启动,运行后,多核启动,操作系统正常运行。