Skip to content
Runtime
Go back

MIT 6.S081 Lecture 2. OS organization and system calls

Edit page

课堂笔记

每个进程有自己独立的页表,因而只能访问出现在页表中的物理地址,实现了进程之间的物理地址隔离

关于某个系统调用是否合法,这一步主要在内核态进行检验,例如检查write的参数是否合法

如何理解进程是对CPU的一种抽象

**物理层面:**CPU 的程序计数器(PC,Program Counter)在一个时钟周期内只能指向一条指令

并发执行:由操作系统的时间片轮转(Time Slicing)和抢占机制实现了并发执行

**虚拟化:**通过虚拟化(Virtualization)CPU,让进程认为自己在独占CPU

**快照机制:**当 OS 决定停止执行进程 A 改为执行进程 B 时,它会将当前 CPU 的所有寄存器值保存到进程 A 的 PCB(Process Control Block,进程控制块) 中,再次运行A时,就可以回到之前的状态

**复用方式:**具体的,复用CPU的方式有波分复用、时分复用、码分复用等

Q&A

xv6的启动过程

有点复杂,这里分两个版本来讲

简单来说

可以把这个过程简化为 4 个核心步骤

1. 找个“落脚点” (汇编阶段)


2. 身份“降级” (机器模式)


3. 大管家“装修” (内核初始化)


4. 开启“正式营业” (调度循环)


总结一下

阶段核心任务状态变化
第一步 (entry.S)分配栈空间硬件状态 \rightarrow 环境就绪
第二步 (start.c)切换到内核权限Machine Mode \rightarrow Supervisor Mode
第三步 (main.c)初始化内存和设备只有代码 \rightarrow 完整系统架构
第四步 (main.c)运行第一个程序内核初始化 \rightarrow 用户可用

完整过程

结合xv6的代码来讲

第一阶段:硬件加载与入口 (entry.S)

当 QEMU 模拟器启动时,它会将内核加载到内存地址 0x80000000 处。此时,所有的 CPU 核心(在 RISC-V 中称为 hart,即 Hardware Thread)都会从这个地址开始执行。

  1. 设置 C 语言运行环境:由于 C 语言的函数调用依赖于栈(Stack),而硬件复位时栈指针 sp 是未定义的,所以 _entry 的首要任务是为每个 CPU 分配一个独立的栈。

  2. 计算栈地址:内核在 start.c 中预定义了一个 4096 字节倍数的数组 stack0。entry.S 通过以下公式为每个核心设置 sp:

    sp = stack0 + (hartid + 1) * 4096。

  3. 跳转到 C 代码:设置好栈后,核心会通过 call start 进入 start.c

第二阶段:特权模式切换 (start.c)

RISC-V 硬件在复位后默认处于最高特权级——机器模式(Machine Mode)。为了系统的安全和稳定,内核通常运行在监管者模式(Supervisor Mode),而用户程序运行在用户模式(User Mode)start() 函数的核心任务就是将特权级从 Machine Mode 降低到 Supervisor Mode。

第三阶段:内核初始化 (main.c)

当代码进入 main() 时,系统已经运行在 Supervisor Mode。为了避免多个 CPU 同时初始化全局资源造成冲突,xv6 采用了“主核负责,从核等待”的策略:

  1. 主核(Hart 0)初始化全局资源
    • consoleinit() / printfinit():初始化控制台,这样我们才能在屏幕上看到输出。
    • kinit():初始化物理内存分配器。
    • kvminit():创建内核页表。
    • userinit()最关键的一步,创建第一个用户进程(通常是执行 initcode.S)。
    • 同步信号:主核完成上述任务后,将全局变量 started 置为 1,并执行内存屏障 __sync_synchronize() 确保所有核心都能看到这个变化。
  2. 从核(Other Harts)等待与局部初始化
    • 非 0 号核心会在 while(started == 0) 循环中自旋等待。
    • 一旦 started 变为 1,它们会执行自己核心相关的局部初始化,例如 kvminithart()(开启本核心的分页机制)和 trapinithart()(设置本核心的中断向量表)。

第四阶段:进入调度器

最后,所有的核心(包括主核和从核)都会调用 scheduler()


如何从用户态(User Mode)转换到内核态(Kernel Mode)

在操作系统中,从用户态(User Mode)转换到内核态(Kernel Mode)是保证系统安全与稳定的核心机制。对于正在学习 MIT 6.S081 并准备 C++ 面试的你来说,这不仅是一个操作系统概念,更是理解高性能编程(如减少上下文切换开销)的基础。 主要有三种途径可以触发这种转换:

  1. 系统调用 (System Call) 这是最常见的一种方式,是用户程序主动申请内核服务。
  1. 异常 (Exceptions) 这是由 CPU 在执行指令时检测到的被动转换。
  1. 外设中断 (Hardware Interrupts) 这是由外部设备触发的异步事件。

对于课程中提到qemu的作用的部分,补充一些计算机组成原理的知识

  1. 核心架构:冯·诺依曼模型 (Von Neumann Architecture) 计算机的基础是“存储程序”概念。它主要由五部分组成:

面试点: 在 C++ 中,程序的执行实际上就是将编译好的机器码从硬盘加载到内存,再由 CPU 依次读取。

  1. 寄存器 (Registers):CPU 的“贴身口袋” 寄存器是 CPU 内部存储速度最快的存储单元。由于内存(RAM)距离 CPU 相对较远且速度慢,CPU 需要寄存器来暂存正在处理的数据。 常见的寄存器包括:
  1. 指令执行周期 (Instruction Cycle) 一条指令从产生到完成,通常经历以下四个阶段: 第一步:取指令 (Fetch) CPU 根据 PC 寄存器 中的地址,去内存里找到对应的指令,并将其读入到 IR (指令寄存器) 中。取完后,PC 会自动递增,指向下一条指令。 第二步:译码 (Decode) 控制器 (CU) 对 IR 中的指令进行“翻译”。它需要搞清楚:

Edit page
Share this post on:

Previous Post
MIT 6.S081 Lecture 0. 前提说明
Next Post
个人投资