Post

OSTEP 第六章笔记

ch6 机制:受限直接执行笔记梳理

OSTEP 第六章笔记

虚拟化CPU?

  • 基本思想:运行一个进程一段时间,然后运行另一个进程,如此轮换 → 以某种方式时分共享(time sharing) CPU
  • 挑战
    • 性能:高性能,不增加系统开销
    • 控制权:保持操作系统对 CPU 的控制权

基本技巧:受限直接执行(limited direct execution)

  • 直接执行:直接在 CPU 上运行程序
    • 在进程列表中为其创建一个进程条目,为其分配一些内存,将程序代码(从磁盘)加载到内存中,找到入口点(main()函数或类似的),跳转到那里,并开始运行用户的代码
  • 问题1:受限制的操作
  • 问题2:在进程之间切换

问题1:受限制的操作

我们采用的办法是引入一种新的处理器模式,称为用户模式。

  • 在用户模式下运行的代码会受到限制。

内核模式

  • 操作系统(或内核)就以这种模式运行
  • 在内核模式下运行的代码可以做它喜欢的事情,包括特权操作,如发出I/O请求和执行所有类型 受限指令

如果用户希望执行某种特权操作,应该怎么做?

  • 为了实现这一点,几乎所有的现代硬件都提供了用户程序执行系统调用的能力。系统调用允许内核小心地向用户程序暴露某些关键功能,例如访问文件系统、创建和销毁进程、与其他进程通信,以及分配更多内存。大多数操作系统提供几百个调用。

要执行系统调用,程序必须执行特殊的 trap 指令(跳入内核并将特权级别提升到内核模式)。

完成后,操作系统调用一个特殊的 return-from-trap 指令(返回到发起调用的应用程序,同时将特权级别降低到用户模式)

执行 trap 时,硬件需要存储足够的调用者寄存器,以便 OS 发出 return-from-trap 时能够正确返回。

例如,在x86上,处理器会将程序计数器、标志和其他一些寄存器推送到每个进程的内核栈上。return-from-trap 指令将从栈弹出这些值,并恢复执行用户模式程序。

trap 如何知道在 OS 内运行哪些代码?

  • 内核通过在启动时设置陷入表(trap table)来实现。当机器启动时,它在内核模式下执行。操作系统做的第一件事,就是告诉硬件在发生某些异常事件(如硬盘中断/键盘中断/程序进行系统调用)时要运行哪些代码。

硬件通过提供不同的执行模式来协助操作系统。

  • 用户模式:应用程序不能完全访问硬件资源
  • 内核模式:操作系统可以访问机器的全部资源

还提供了特殊指令。

  • 陷入(trap): 陷入内核
  • 从陷入返回(return-from-trap): 从内核模式切换回用户模式

问题2:在进程之间切换

操作系统如何重新获得CPU的控制权,以便它可以在进程之间切换?

a. 协作方式:等待系统调用

  • 在协作调度系统中,OS通过等待系统调用,或某种非法操作发生,从而重新获得CPU的控制权
  • 但是如果某个进程假如无限循环从不进行系统调用呢?

b. 非协作方式:操作系统进行控制

  • 利用 时钟中断 重新获得控制权

时钟中断(timer interrupt)

  • 时钟设备可以被编程为每个几毫秒产生一次中断
  • 中断发生时,当前正在运行的进程停止,操作系统中预先设置的中断处理程序会运行

首先,在启动时,操作系统必须通知硬件哪些代码在发生中断时运行。

其次,在启动过程中,操作系统也必须启动时钟。

在中断发生时,硬件要为正在运行的程序保存足够的状态,以便随后 return-from-trap 指令能够恢复正在运行的程序。

  • 各种寄存器因此被保存(e.g. 进入内核栈)

保存和恢复上下文

如果调度程序决定切换到另一个程序,OS 会执行执行一些底层代码,即所谓的上下文切换

上下文切换(context switch):操作系统要做的就算为当前正在执行的进程保存一些寄存器的值(例如,到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)

上下文切换的过程:

  • 为了保存当前正在运行的进程的上下文,操作系统会执行一些底层汇编代码,来保存通用寄存器、程序计数器,以及当前正在运行的进程的内核栈指针,然后恢复寄存器、程序计数器,并切换内核栈,供即将运行的进程使用。
  • 通过切换栈,内核在进入切换代码调用时,是一个进程的上下文,在返回时,是另一个进程的上下文
  • 当操作系统最终执行return-from-trap指令时,即将执行的进程编程了当前运行的进程
  • 至此上下文切换完成

进程上下文切换时有两种寄存器保存/恢复

  • 硬件保存用户上下文(运行进程的用户寄存器)
    • 发生时钟中断时
    • 硬件隐式地保存在用户模式下运行时的寄存器状态(程序计数器、通用寄存器等)
      • 该程序运行时的数据、计算结果、下一条指令的地址等
    • 保存到该进程的内核栈
  • OS保存内核上下文(内核寄存器)
    • 当操作系统决定从A切换到B时
    • OS保存在内核模式下所使用的寄存器状态
    • 保存在 A 的进程控制块 (PCB) 中

解决并发问题(这里我们先只是简单介绍一下)

  • 操作系统可能简单的决定,在中断期间禁止中断。(当然,操作系统这样做必须小心。禁用中断事件过长可能导致丢失中断)
  • 操作系统还开发了许多复杂的加锁方案

小结

实现 CPU 虚拟化的关键底层机制:受限直接执行(limited direct execution)

基本思路很简单:就让你想运行的程序在 CPU 上运行,但首先确保设置好硬件,以便在没有操作系统帮助的情况下限制进程可以执行的操作。

OS 先设置陷阱处理程序并启动时钟中断,然后在受限模式下运行进程。只有在执行特权操作,或者当它们独占 CPU 时间过长并因此需要切换时,才需要操作系统干预。

TIPS:重新启动是有用的

  • 它让程序回到已知的状态
  • 可以回收旧的或泄漏的资源(例如内存)

为什么系统调用看起来像过程调用:

它是一个过程调用,但隐藏在过程调用内部的是著名的 trap 指令。更具体的说,例如当你调用 open() 时,你正在执行的是 C 库的过程调用。C库中进行系统调用的部分是用汇编手工编码的

This post is licensed under CC BY 4.0 by the author.

Trending Tags