Starting xv6 and the first process
Starting xv6 and the first process
_entry
Loader将xv6内核加载到物理地址0x80000000
,然后CPU从kernel/entry.S
开始执行代码。在kernel/start.c
中为每一个CPU都定义了一个4096字节的栈,将栈顶的地址存入sp
寄存器中,然后调用kernel/start.c
中定义的start()
函数。
# kernel/entry.S
# qemu -kernel loads the kernel at 0x80000000
# and causes each CPU to jump there.
# kernel.ld causes the following code to
# be placed at 0x80000000.
.section .text
_entry:
# set up a stack for C.
# stack0 is declared in start.c,
# with a 4096-byte stack per CPU.
# sp = stack0 + (hartid * 4096)
la sp, stack0
li a0, 1024*4
csrr a1, mhartid
addi a1, a1, 1
mul a0, a0, a1
add sp, sp, a0
# jump to start() in start.c
call start
spin:
j spin
start
start()
函数执行了一些只可以在machine
态下执行的配置,然后转换到supervisor
态。
从machine
态转换到supervisor
态是通过mret
指令实现的。mret
指令的作用是在之前的函数A从supervisor
态转换到machine
态后,重新转回supervisor
态继续执行函数A剩下的代码。而start
函数之前并没有这么一个函数调用,所以通过修改mstatus
寄存器的值将之前的状态设置为supervisor
态,并且修改mepc
寄存器的值为main
函数的地址,设置页表寄存器satp
的值为0
使得在supervisor
态下不可以进行虚拟地址转换。
kernel main
初始化设备和子系统,然后调用kernel/proc.c
中的userinit()
函数。
userinit
调用了user/initcode.S
。
initcode.S
通过调用exec
函数执行user/init.c
中的main()
函数。
user main
打开标准输入、输出、错误流,并且通过fork()
创建一个子进程,在子进程中调用exec()
执行sh
程序创建shell。