xv6 第二章_操作系统架构

第二章 操作系统架构

操作系统必须满足三个要求:多路复用、隔离和交互

  • 多路复用:即使进程数量比硬件处理器多,操作系统也必须确保每个进程都能运行。
  • 隔离:一个进程有故障或者错误,不应该影响到其他进程。
  • 交互 :进程之间可以进行刻意的交互;如通过管道实现。

一、抽象系统资源

假如不抽象硬件资源,每个应用程序能够直接使用硬件资源。这种情况好处是每个应用程序能够直接和硬件交互,应用程序自己决定如何使用硬件。

但是如果多个应用程序同时运行,就必须考虑它们对硬件资源的使用顺序和占用情况,如何协调多个应用程序的运行是一件极其麻烦的事情。

一些嵌入式设备或者实时操作系统就是这样组织的。

更好的办法是禁止应用程序直接访问敏感的硬件资源 ,而是将资源抽象为服务。

应用程序请求这些服务,而服务由操作系统来执行并返回结果。

这样用户程序就不用考虑服务是如何实现的,只需要使用即可;至于多个应用程序如何运行,也是操作系统来协调组织。

Unix进程使用 exec 来构建它们的内存映像,而不是直接与物理内存交互。

操作系统来决定将一个进程放在内存中的哪里。
exec 系统调用依赖操作系统的文件系统,使用户能把可执行程序作为普通文件存储、管理并方便地运行。

Unix进程之间的许多交互形式都是通过文件描述符 实现的。

文件描述符不仅抽象了许多细节(例如,管道或文件中的数据存储在哪里),而且还以简化交互的方式进行了定义。

例如,如果流水线中的一个应用程序失败了,内核会为流水线中的下一个进程生成文件结束信号(EOF)。

示例:

复制代码
cat nofile.txt | grep hello

假如 cat nofile.txt 失败了(文件不存在),cat 程序退出时,它的输出端(即管道的写端)关闭。

当内核检测到这个 pipe 的所有写端都关闭时,会在管道的读端返回 EOF 信号,这样 grep 程序发现没有数据可读,也会结束执行。


二、用户态、核心态和系统调用

应用程序出错不应该影响操作系统或者其他应用程序;操作系统必须保证应用程序不能修改操作系统的数据结构和指令。

这就是操作系统需要做到的隔离

CPU 为隔离提供硬件支持。

RISC-V 架构的 CPU 有三种执行模式:

  • 机器模式(Machine Mode)
  • 管理模式(Supervisor Mode)
  • 用户模式(User Mode)

想要调用内核函数的应用程序必须切换到内核。

RISC-V 提供一个特殊指令 ecall,将 CPU 从用户模式切换到管理模式,并在内核指定的入口点进入内核。

用户程序调用 ecall,只是向内核请求服务。


三、内核组织

  • 宏内核(Monolithic Kernel)
    所有的系统调用都以管理模式运行;但用户态与内核态之间的切换会带来一定开销。
  • 微内核(Microkernel)
    大部分系统调用在用户态下执行,只有少部分最基础的系统调用在内核态执行。

四、Xv6代码架构

Xv6 源码位于 kernel 目录下,按照模块化的概念划分文件:

文件 描述
bio.c 文件系统的磁盘块缓存
console.c 连接到用户的键盘和屏幕
entry.S 首次启动指令
exec.c exec() 系统调用
file.c 文件描述符支持
fs.c 文件系统
kalloc.c 物理页面分配器
kernelvec.S 处理来自内核的陷入指令以及计时器中断
log.c 文件系统日志记录以及崩溃修复
main.c 在启动过程中控制其他模块初始化
pipe.c 管道
plic.c RISC-V 中断控制器
printf.c 格式化输出到控制台
proc.c 进程和调度
sleeplock.c 让出 CPU 的锁
spinlock.c 不让出 CPU 的锁
start.c 早期机器模式启动代码
string.c 字符串和字节数组库
swtch.c 线程切换
syscall.c 分发系统调用
sysfile.c 文件相关的系统调用
sysproc.c 进程相关的系统调用
trampoline.S 用于在用户和内核之间切换的汇编代码
trap.c 对陷入指令和中断进行处理并返回的 C 代码
uart.c 串口控制台设备驱动程序
virtio_disk.c 磁盘设备驱动程序
vm.c 管理页表和地址空间

五、进程概述

Xv6(和其他 Unix 操作系统一样)中的隔离单位是一个进程

内核用来实现进程的机制包括:

  • 用户/管理模式标志
  • 地址空间
  • 线程的时间切片

其中 trampolinetrapframe 位于每个进程用户内存空间的顶层,各占一页。

虽然这两页映射在每个进程的虚拟地址空间中,但其内容由内核管理。

  • Trampoline 页(跳板页)
    包含用户态和内核态切换时执行的汇编代码。
    每个进程的虚拟地址空间都映射相同的 trampoline 物理页,因此该页是所有进程共享的。
  • Trapframe 页(陷入帧)
    当用户程序陷入内核时,内核需要一个地方保存用户寄存器的值,以便执行完后恢复现场。
    每个进程都有自己的 trapframe 结构,但用户程序不能访问该页,只有内核才有读写权限。

为什么 trampoline 和 trapframe 放在进程用户地址空间的顶部?

  • 固定位置方便切换,不需要查表。
  • 每个进程切换时只需切换页表,不需更改映射逻辑。

每个进程都有一个执行线程来执行指令。

用户页表由内核分配并存放在内核控制的物理内存中,用于描述用户虚拟空间的映射关系,但用户程序无法访问或修改它。


六、启动 XV6 和第一个进程(代码)

使用 GDB 调试从 xv6 上电到启动第一个进程的过程。

当 RISC-V 计算机上电时,它会初始化自己并运行一个存储在只读内存中的引导加载程序

引导加载程序将 xv6 内核加载到内存中,然后在机器模式 下从 _entry(kernel/entry.S) 开始运行 xv6。

此时页式硬件处于禁用模式,虚拟地址直接映射到物理地址,即使用的是物理地址。

  • 引导加载程序将内核加载到物理地址 0x80000000
  • 内核从 _entry 开始执行
  • 每个 CPU 都会从 _entry 开始执行
  • _entry 为每个 CPU 设置栈指针 sp,然后跳转到 start()kernel/start.c

start() 函数执行一些仅在机器模式下允许的配置,然后切换到管理模式,并跳转到 main() 函数(kernel/start.c)执行。

start() 中,会:

  • 禁用虚拟地址转换(将页表寄存器 satp 设置为 0)
  • 将所有中断和异常委托给管理模式

main() 函数对内核进行初始化,并初始化第一个用户进程 init

然后 initcode.S 通过 exec 执行 init 进程(user/init.c),
init 进程会 forkshell 进程,此时 shell 就运行起来了,

后续的用户进程都由 shell 来 fork

进程树如下:

复制代码
└─ [kernel]  ← 内核启动(main函数)
     └─ initcode (user/initcode.S)
          └─ init (user/init.c)
               └─ sh (user/sh.c)
                    ├─ cat (user/cat.c)
                    ├─ echo (user/echo.c)
                    ├─ ls (user/ls.c)
                    ├─ grep (user/grep.c)
                    ├─ kill (user/kill.c)
                    ├─ rm (user/rm.c)
                    ├─ sleep (user/sleep.c)
                    └─ ...(用户运行的其他命令)

七、真实世界

许多 Unix 系统都采用宏内核

虽然 Linux 的一些操作系统功能作为用户级服务器运行(如窗口系统),但它仍属于宏内核架构。

而 L4、Minix 和 QNX 的内核被组织成一个带有多个服务器的微内核,这种设计在嵌入式设备中得到了广泛应用。

xv6 不支持一个进程创建多个线程。


七、真实世界

许多 Unix 系统都采用宏内核

虽然 Linux 的一些操作系统功能作为用户级服务器运行(如窗口系统),但它仍属于宏内核架构。

而 L4、Minix 和 QNX 的内核被组织成一个带有多个服务器的微内核,这种设计在嵌入式设备中得到了广泛应用。

xv6 不支持一个进程创建多个线程。

但现代操作系统都支持在一个进程中创建多个线程。

相关推荐
2401_841495646 小时前
【操作系统】计算机系统概述
操作系统·发展历程·虚拟化技术·运行环境·运行机制·系统结构·引导流程
小志biubiu13 小时前
【Linux】Ext系列文件系统
linux·服务器·c语言·经验分享·笔记·ubuntu·操作系统
又过一个秋1 天前
dpdk-3.hash表CURD
后端·c
航Hang*2 天前
第1章:初识Linux系统——第8节:查看/修改权限控制和ACL
linux·运维·服务器·笔记·操作系统
煤球王子2 天前
学而时习之:C语言中的函数指针
c
unspn2 天前
选择语句if
c
煤球王子2 天前
学而时习之:C语音中的指针
c
沐怡旸2 天前
【计算机通识】为什么有用户态和内核态之分?
操作系统
帅锅锅0073 天前
SeLinux Type(类型)深度解析
android·操作系统