Skip to content

main.c

TIP

包含 xv6 内核所需的基本类型定义,例如 uint、ushort 等。

#include "types.h"

TIP

包含内核参数的定义,例如最大进程数 (NPROC)、最大文件数 (NOFILE) 等。

#include "param.h"

TIP

包含内存布局的定义,例如内核基地址 (KERNBASE)、物理内存结束地址 (PHYSTOP) 等。

#include "memlayout.h"

TIP

包含 RISC-V 架构相关的定义和内联函数,例如用于读写控制寄存器的函数。

#include "riscv.h"

TIP

包含内核中所有函数的原型声明,充当内核的全局头文件。

#include "defs.h"

TIP

一个易失性静态整型变量,用于指示内核是否已完成在主 CPU 上的初始化。 volatile 关键字告诉编译器不要对该变量的访问进行优化(例如,缓存到寄存器中), 因为它的值可能会被另一个 CPU(硬件线程)并发地修改。

volatile static int started = 0;

TIP

SBI (Supervisor Binary Interface) 调用函数。 这是一个内联函数,用于通过 ecall 指令从 supervisor 模式陷入到 machine 模式, 以便执行由 RISC-V SBI 规范定义的特权操作,例如关闭系统。 which: SBI 扩展 ID 和功能 ID arg0, arg1, arg2: 传递给 SBI 调用的参数

static inline void
sbi_call(uint64 which, uint64 arg0, uint64 arg1, uint64 arg2)
{

TIP

将参数加载到指定的寄存器中。根据 RISC-V 调用约定, a0-a2 用于传递参数,a7 用于传递系统调用/SBI 调用号。

  register uint64 a0 asm ("a0") = arg0;
  register uint64 a1 asm ("a1") = arg1;
  register uint64 a2 asm ("a2") = arg2;
  register uint64 a7 asm ("a7") = which;
  

TIP

ecall 指令会触发一个环境调用异常,将控制权从 S-mode 转移到 M-mode。 M-mode 下的陷阱处理程序会处理这个调用。

  asm volatile ("ecall"
                : "+r" (a0)
                : "r" (a1), "r" (a2), "r" (a7)
                : "memory");
}

TIP

内核的 C 代码入口点。 在 _entry (entry.S) 完成了基本的机器模式设置后, 所有 CPU 都会以 supervisor 模式跳转到这里执行。

void
main()
{
  extern const char build_hash[];

TIP

cpuid() 返回当前硬件线程(hart)的 ID。 hart 0 被指定为主 CPU,负责执行一次性的系统级初始化。

  if(cpuid() == 0){
    consoleinit();
    printfinit();
    printf("\n");
    printf("xv6 内核正在启动\n");
    printf("构建哈希: %s\n", build_hash);
    printf("\n");
    kinit();
    kvminit();
    kvminithart();
    procinit();
    trapinit();
    trapinithart();
    plicinit();
    plicinithart();
    binit();
    iinit();
    fileinit();
    virtio_disk_init();
    userinit();
    

TIP

__sync_synchronize() 是一个内存屏障。 它确保在此之前的所有内存写入操作都已完成,并且对其他 CPU 可见, 然后才执行 started = 1

    __sync_synchronize();
    started = 1;
  } else {

TIP

其他 hart (非 hart 0) 会在此处自旋等待。

    while(started == 0)
      ;

TIP

内存屏障,确保能正确读取到主 CPU 对 started 的写入。

    __sync_synchronize();
    printf("hart %d 启动\n", cpuid());
    

TIP

为当前 hart 设置和启用其自己的内核页表和陷阱。

    kvminithart();
    trapinithart();
    plicinithart();
  }

TIP

所有 CPU(包括主 CPU 和其他 CPU)在完成各自的初始化后, 都会调用 scheduler() 函数,开始无限循环地调度和运行进程。

  scheduler();
}