源代码阅读
文件名称 | 基本作用 |
---|---|
bootasm.S | 16-bit和32-bit汇编代码,XV6引导加载器 |
bootmain.c | 主要是void bootmain(void)函数 |
x86.h | 允许C代码使用特殊的x86指令 |
trap.c | 陷入指令的C语言处理接口 |
trapasm.S | 陷入指令的汇编逻辑 |
vectors.pl&vectors.S | 256个中断描述符 |
syscall.c | 声明系统调用接口 |
syscall.h | 系统调用号 |
sysproc.c | 定义syscall.c中声明的系统调用接口sys_fork等 |
proc.c | 进程管理,定义proc结构,allocproc, userinit, growproc, fork, exit, wait, scheduler, yield等函数 |
启动部分:
bootams.S启动第一个CPU,切换到32-bit保护模式。BIOS将此代码从硬盘的第一个扇区加载到物理地址为0x7c00的内存中,并开始以实模式执行。物理地址线A20绑定到0,解开后从实模式切换到保护模式,使用引导GDT,将虚拟地址直接映射到物理地址。bootmain.c中调用bootmain(),从扇区1开始从磁盘加载一个ELF内核映像,然后跳转到内核入口例程。main.c启动xv6系统。
理论知识
用户态和内核态
Q:什么是用户态和内核态?两者有何区别?什么是中断和系统调用?两者有何区别?计算机在运行时,是如何确定当前处于用户态还是内核态的?
CPU有两种权限状态,一种是内核态,亦称管态、核心态,是操作系统内核所运行的模式,特权级最高,可以调用任何CPU指令,访问任意内存地址及外部设备等。另一种是用户态,亦称目态,是一个进程在执行用户自己的代码时所处的状态,特权级最低,进程只能访问到自己被分配的内存,在执行指令时也可能被高优先级的指令抢先。大多数用户运行的程序都是在用户态下运行的。
用户态和内核态的主要区别就在于特权级的不同,用户态无法调用一些内核的指令。
系统调用是用户在编程时可以调用的操作系统功能的一些子程序,是操作系统提供给编程人员从用户态陷入内核态的唯一接口,通过中断/异常机制实现。
中断/异常是CPU对系统发生的某一事件做出的一种反应,CPU在收到中断请求时暂停当前执行的程序,保留现场,自动转去执行相应事件的处理程序,处理完成后恢复现场返回断点,继续执行被打断的程序。系统调用是一种异常。
中断和系统调用的区别在于系统调用会让CPU一开始就进入内核态,以执行相应的操作。中断涵盖的范围更广,包括I/P中断、时钟中断、硬件故障、页错误、保护性异常、断点指令、其他程序性异常等。
在计算机系统运行时,可以通过特权级的区分来区分当前是用户态还是内核态。
中断与XV6中的硬件实现
Q:计算机开始运行阶段就有中断吗?XV6的中断管理是如何初始化的?XV6是如何实现内核态到用户态的转变的?XV6 中的硬件中断是如何开关的?实际的计算机里,中断有哪几种?
计算机在开始运行阶段就已经有中断了,由BIOS支持。
初始化:xv6开始运行时在bootasm.S中用汇编语言里BIOS提供的cli命令禁止中断。
//bootasm.S
.globl start
start:
cli # BIOS enabled interrupts; disable
之后在main.c中初始化一系列中断机制。
程序 | 作用 |
---|---|
picinit &oapicinit | 初始化中断控制器 |
consoleinit & uartinit | I/O中断 |
uartinit | 设备端口中断 |
tvinit | 初始化中断描述符表IDT |
idtinit | disk在调度开始前设置32号时钟中断 |
最后在mpmain中调用scheduler,通过sti开启中断(之前在bootasm.S中被cli关闭),初始化完成。
内核态到用户态转变:
proc.c的userinit函数设置第一个进程的trapframe中的cs, ds, es, ss为DPL_USER(用户模式),即设置为用户态。
//proc.c
void
userinit(void){
...
p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
p->tf->ds =