MIT-6.S081 Xv6 Chapter 2 Operating System Organization

196 阅读3分钟

Operating System Organization

User mode and supervisor mode

​ 为了实现进程隔离,RISC-V CPU在硬件上提供3种执行命令的模式:machine mode, supervisor mode, user mode

  1. machine mode的权限最高,CPU以machine mode启动,machine mode的主要目的是为了配置电脑,之后立即切换到supervisor mode
  2. supervisor mode运行CPU执行privileged instructions,比如中断管理、对存储页表地址的寄存器进行读写操作、执行system call。运行在supervisor mode也称为在kernel space中运行。
  3. 应用程序只能执行user mode指令,比如改变变量、执行util function。运行在user mode也称为在user space中运行。要想让CPU从user mode切换到supervisor mode,RISC-V提供了一个特殊的ecall指令,要想从supervisor mode切换到user mode,调用sret指令

Kernel organization

monolithic kernel:整个操作系统在kernel中,所有system call都在supervisor mode下运行。xv6是一个monolithic kernel

micro kernel:将需要运行在supervisor mode下的操作系统代码压到最小,保证kernel内系统的安全性,将大部分的操作系统代码执行在user mode下。

image-20230504152619480.png 文件系统是一个user-level的进程,为其他进程提供服务,因此也叫做server

Process

​ 隔离的单元叫做进程,一个进程不能够破坏或者监听另外一个进程的内存、CPU、文件描述符,也不能破坏kernel本身。

​ 为了实现进程隔离,xv6提供了一种机制让程序认为自己拥有一个独立的机器。一个进程为一个程序提供了一个私有的内存系统,或address space,其他的进程不能够读/写这个内存。xv6使用page table(页表)来给每个进程分配自己的address space,页表再将这些address space,也就是进程自己认为的虚拟地址(virtual address)映射到RISC-V实际操作的物理地址(physical address)

image-<img src="20230504152906222" alt="" width="50%" />.png ​ 虚拟地址从0开始,往上依次是指令、全局变量、栈、堆。RISC-V上的指针是64位的,xv6使用低38位,因此最大的地址是2381=0x3fffffffff=MAXVA2^38−1=0x3fffffffff=MAXVA

​ 进程最重要的内核状态:1. 页表 p->pagetable 2. 内核堆栈p->kstack 3. 运行状态p->state,显示进程是否已经被分配、准备运行/正在运行/等待IO或退出每个进程中都有线程(thread),是执行进程命令的最小单元,可以被暂停和继续。

​ 每个进程有两个堆栈:用户堆栈(user stack)和内核堆栈(kernel stack)。当进程在user space中进行时只使用用户堆栈,当进程进入了内核(比如进行了system call)使用内核堆栈.

Starting the first process

​ RISC-V启动时,先运行一个存储于ROM中的bootloader程序kernel.ld来加载xv6 kernel到内存中,然后在machine模式下从_entry开始运行xv6。bootloader将xv6 kernel加载到0x80000000的物理地址中,因为前面的地址中有I/O设备。

​ 在_entry中设置了一个初始stack,stack0来让xv6执行kernel/start.c。在start函数先在machine模式下做一些配置,然后通过RISC-V提供的mret指令切换到supervisor mode,使program counter切换到kernel/main.c

main先对一些设备和子系统进行初始化,然后调用kernel/proc.c中定义的userinit来创建第一个用户进程。这个进程执行了一个initcode.S的汇编程序,这个汇编程序调用了exec这个system call来执行/init,重新进入kernel。exec将当前进程的内存和寄存器替换为一个新的程序(/init),当kernel执行完毕exec指定的程序后,回到/init进程。/init(user/init.c)创建了一个新的console device以文件描述符0,1,2打开,然后在console device中开启了一个shell进程,至此整个系统启动了。