简述
操作系统支持多进程任务,并且每个进程之前还应当隔离,但又在必要的时候进行交互。
操作系统必须满足的三个要求:多路复用、隔离和交互
抽象系统资源实现隔离
禁止应用程序直接访问敏感的硬件资源,将这些资源抽象为服务,提供接口向应用程序服务。一般来说就是通过系统调用命令来实现的。
在Unix中交互一般是通过文件描述符来实现的。
用户态,核心态和系统调用
对于一个应用程序,操作系统必须保证应用程序不能修改(甚至读取)操作系统的数据结构和指令,以及应用程序不能访问其他进程的内存。
而CPU就提供了三种模式来满足操作系统的隔离性:用户模式、管理模式、机器模式(权限从低到高)。
当应用程序执行指令时,只能在用户空间中进行。当应用程序在管理模式下运行,其将会在内核空间中运行。
CPU会有一个指令用来切换用户模式和管理模式,需要注意的是,对于进入内核的入口点需要进行检查,我们需要对要进入内核操作的函数检测参数的合法性,如果让用户模式下的应用程序决定入口点,可能会导致跳过参数检查
宏内核
将整个操作系统都放在内核运行,操作系统每个部分都可以直接从操作硬件,这样的组织方式叫做宏内核。但当操作系统中的Bug出现时,将导致计算机的崩溃
当将大部分操作系统从管理模式剥离出来,而放在用户模式下进行时,这种内核组织称为微内核
最简单的单机微内核模式广泛采用C/S模式搭建,即用户的应用程序作为客户端,而操作系统在用户层的功能形成一组由多个进程组成的服务器。
当用户程序想要访问文件系统时,会通过内核层(管理模式)向文件系统进程进行通信。
进程
在XV6中隔离的作用就是隔离一个个进程-->防止两个进程之间的互相影响,以及对内核的操作。为了满足隔离,进程对它负责的程序提供了一个私有内存空间以及让应用程序认为自己独享一个CPU。
内存空间
xv6通过页表来实现对进程的地址空间分配。RISC-V将虚拟地址(RISC-V操作的地址)映射到物理地址上(物理内存上的分区)。
由于每个进程会维持一个单独的内存,所以每个进程拥有一个单独的页表。从虚拟地址0开始向高地址依次是指令集,全局变量,栈区,堆区。而虚拟地址的最大范围受到RISC-V(64位指针)、硬件映射关系的限制(只是用低39位)以及xv6只是用38位,导致最大虚拟地址为0x3fffffffff。
并且还预留了trampoline(用户模式和管理模式的切换)和trapframe
内核对进程的保存
内核将进程分割为多个状态片段保存,比如:页表、内核栈区、运行状态
线程
每个进程拥有一个线程来执行当前该进程的指令,类似于pc指针,在cpu对进程进行切换后,前一个进程中的线程可以挂起,然后当cpu切换回来之后可以从当前线程处直接恢复。
进程拥有两个栈区:用户栈区和内核栈区分别对应其在用户态和管理态下的运行。
进程可以通过执行ecall指令进行sys_call然后pc指针指向内核定义的切换入口点,将入口点处的代码返回到当前进程的内核栈中,当sys_call结束后,会切换会用户栈,然后调用sret指令返回用户态