# 2.6 代码（启动XV6和第一个进程）

为了使xv6更加具体，我们将概述内核如何启动和运行第一个进程。接下来的章节将更详细地描述本概述中显示的机制。

当RISC-V计算机上电时，它会初始化自己并运行一个存储在只读内存中的引导加载程序。引导加载程序将xv6内核加载到内存中。然后，在机器模式下，中央处理器从`_entry` (***kernel/entry.S***:6)开始运行xv6。Xv6启动时页式硬件（paging hardware）处于禁用模式：也就是说虚拟地址将直接映射到物理地址。

加载程序将xv6内核加载到物理地址为`0x80000000`的内存中。它将内核放在`0x80000000`而不是`0x0`的原因是地址范围`0x0:0x80000000`包含I/O设备。

`_entry`的指令设置了一个栈区，这样xv6就可以运行C代码。Xv6在***start. c (kernel/start.c:11)***文件中为初始栈***stack0***声明了空间。由于RISC-V上的栈是向下扩展的，所以`_entry`的代码将栈顶地址`stack0+4096`加载到栈顶指针寄存器`sp`中。现在内核有了栈区，`_entry`便调用C代码`start`(***kernel/start.c***:21)。

函数`start`执行一些仅在机器模式下允许的配置，然后切换到管理模式。RISC-V提供指令`mret`以进入管理模式，该指令最常用于将管理模式切换到机器模式的调用中返回。而`start`并非从这样的调用返回，而是执行以下操作：它在寄存器`mstatus`中将先前的运行模式改为管理模式，它通过将`main`函数的地址写入寄存器`mepc`将返回地址设为`main`，它通过向页表寄存器`satp`写入0来在管理模式下禁用虚拟地址转换，并将所有的中断和异常委托给管理模式。

在进入管理模式之前，`start`还要执行另一项任务：对时钟芯片进行编程以产生计时器中断。清理完这些“家务”后，`start`通过调用`mret`“返回”到管理模式。这将导致程序计数器（PC）的值更改为`main`(***kernel/main.c***:11)函数地址。

> [!TIP|label:TIPS]
> **注：**`mret`执行返回，返回到先前状态，由于`start`函数将前模式改为了管理模式且返回地址改为了`main`,因此`mret`将返回到`main`函数，并以管理模式运行

在`main`(***kernel/main.c***:11)初始化几个设备和子系统后，便通过调用`userinit` (***kernel/proc.c***:212)创建第一个进程，第一个进程执行一个用RISC-V程序集写的小型程序：***initcode. S*** (***user/initcode.S:***1)，它通过调用`exec`系统调用重新进入内核。正如我们在第1章中看到的，`exec`用一个新程序（本例中为 `/init`）替换当前进程的内存和寄存器。一旦内核完成`exec`，它就返回`/init`进程中的用户空间。如果需要，`init`(***user/init.c***:15)将创建一个新的控制台设备文件，然后以文件描述符0、1和2打开它。然后它在控制台上启动一个shell。系统就这样启动了。