《操作系统导论》第六章读书笔记:机制:受限直接执行
—— 杭州 2024-03-23 下午
文章目录
- 《操作系统导论》第六章读书笔记:机制:受限直接执行
- 1.受限的直接执行(Limited Direct Execution)
- 2.受限制的操作
- 3.在进程之间切换
- 协作方式:等待系统调用
- 非协作方式:操作系统进行控制
- 保存和恢复上下文
- 4.担心并发吗?
- 5.补充笔记:通用寄存器和程序计数器
- 通用寄存器(General-Purpose Registers)
- 程序计数器(Program Counter, PC)
- 区别和联系
- 6.补充笔记:如果一个进程在CPU上运行,这就意味着操作系统没有运行,如何理解?
为了虚拟化CPU,操作系统需要以某种方式让许多任务共享物理CPU,让它们看起来像是同时运行。基本思想很简单:运行一个进程一段时间,然后运行另一个进程,如此轮换。通过以这种方式时分共享(time sharing)CPU,就实现了虚拟化。
1.受限的直接执行(Limited Direct Execution)
- 受限的直接执行(limited direct execution)。这个概念的“直接执行”部分很简单:只需直接在CPU上运行程序即可。因此,当OS希望启动程序运行时,它会在进程列表中为其创建一个进程条目,为其分配一些内存,将程序代码(从磁盘)加载到内存中,找到入口点(main()函数或类似的),跳转到那里,并开始运行用户的代码。
2.受限制的操作
- 在用户模式(user mode)下,应用程序不能完全访问硬件资源。在内核模式(kernel mode)下,操作系统可以访问机器的全部资源。
- 因此,我们采用的方法是引入一种新的处理器模式,称为用户模式(user mode)。在用户模式下运行的代码会受到限制。例如,在用户模式下运行时,进程不能发出I/O 请求。这
样做会导致处理器引发异常,操作系统可能会终止进程。 - 与用户模式不同的内核模式(kernel mode),操作系统(或内核)就以这种模式运行。
在此模式下,运行的代码可以做它喜欢的事,包括特权操作,如发出I/O 请求和执行所有类
型的受限指令。 - 要执行系统调用,程序必须执行特殊的陷阱(trap)指令。该指令同时跳入内核并将特权级别提升到内核模式。一旦进入内核,系统就可以执行任何需要的特权操作(如果允许),从而为调用进程执行所需的工作。完成后,操作系统调用一个特殊的从陷阱返回(return-from-trap)指令,如你期望的那样,该指令返回到发起调用的用户程序中,同时将特权级别降低,回到用户模式。
- 为什么系统调用看起来像过程调用?原因很简单:它是一个过程调用,但隐藏在过程调用内部的是著名的陷阱指令。更具体地说,当你调用open()(举个例子)时,你正在执行对C库的过程调用。
- 因此,C库中进行系统调用的部分是用汇编手工编码的,因为它们需要仔细遵循约定,以便正确处理参数和返回值,以及执行硬件特定的陷阱指令。
3.在进程之间切换
协作方式:等待系统调用
- 协作(cooperative)方式。在这种风格下,操作系统相信系统的进程会合理运行。运行时间过长的进程被假定会定期放弃CPU,以便操作系统可以决定运行其他任务。
- 大多数进程通过进行系统调用,将CPU的控制权转移给操作系统,例如打开文件并随后读取文件,或者向另一台机器发送消息或创建新进程。像这样的系统通常包括一个显式的yield系统调用,它什么都不干,只是将控制权交给操作系统,以便系统可以运行其他进程。
- 如果应用程序执行了某些非法操作,也会将控制转移给操作系统。例如,如果应用程序以0为除数,或者尝试访问应该无法访问的内存,就会陷入(trap)操作系统。操作系统将再次控制CPU(并可能终止违规进程)。因此,在协作调度系统中,OS通过等待系统调用,或某种非法操作发生,从而重新获得CPU的控制权。
非协作方式:操作系统进行控制
- 时钟中断(timer interrupt)。时钟设备可以编程为每隔几毫秒产生一次中断。产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序(interrupt handler)会运行。此时,操作系统重新获得CPU的控制权,因此可以做它想做的事:停止当前进程,并启动另一个进程。
- 利用时钟中断重新获得控制权即使进程以非协作的方式运行,添加时钟中断(timer interrupt)也让操作系统能够在CPU上重新运行。因此,该硬件功能对于帮助操作系统维持机器的控制权至关重要。
- 操作系统必须通知硬件哪些代码在发生时钟中断时运行。因此,在启动时,操作系统就是这样做的。其次,在启动过程中,操作系统也必须启动时钟,这当然是一项特权操作。一旦时钟开始运行,操作系统就感到安全了,因为控制权最终会归还给它,因此操作系统可以*运行用户程序。时钟也可以关闭(也是特权操作)。
- 硬件在发生中断时有一定的责任,尤其是在中断发生时,要为正在运行的程序保存足够的状态,以便随后从陷阱返回指令能够正确恢复正在运行的程序。这一组操作与硬件在显式系统调用陷入内核时的行为非常相似,其中各种寄存器因此被保存(进入内核栈),因此从陷阱返回指令可以容易地恢复。
保存和恢复上下文
- 既然操作系统已经重新获得了控制权,无论是通过系统调用协作,还是通过时钟中断更强制执行,都必须决定:是继续运行当前正在运行的进程,还是切换到另一个进程。这个决定是由调度程序(scheduler)做出的,它是操作系统的一部分。我们将在接下来的几章中详细讨论调度策略。
- 如果决定进行切换,OS就会执行一些底层代码,即所谓的上下文切换(context switch)。上下文切换在概念上很简单:操作系统要做的就是为当前正在执行的进程保存一些寄存器的值(例如,到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)。这样一来,操作系统就可以确保最后执行从陷阱返回指令时,不是返回到之前运行的进程,而是继续执行另一个进程。
- 为了保存当前正在运行的进程的上下文,操作系统会执行一些底层汇编代码,来保存通用寄存器、程序计数器,以及当前正在运行的进程的内核栈指针,然后恢复寄存器、程序计数器,并切换内核栈,供即将运行的进程使用。通过切换栈,内核在进入切换代码调用时,是一个进程(被中断的进程)的上下文,在返回时,是另一进程(即将执行的进程)的上下文。当操作系统最终执行从陷阱返回指令时,即将执行的进程变成了当前运行的进程。至此上下文切换完成。
- 在此协议中,有两种类型的寄存器保存/恢复。第一种是发生时钟中断的时候。在这种情况下,运行进程的用户寄存器由硬件隐式保存,使用该进程的内核栈。第二种是当操作系统决定从A切换到B。在这种情况下,内核寄存器被软件(即OS)明确地保存,但这次被存储在该进程的进程结构的内存中。后一个操作让系统从好像刚刚由A陷入内核,变成好像刚刚由B陷入内核。
4.担心并发吗?
- 操作系统可能简单地决定,在中断处理期间禁止中断(disable interrupt)。这样做可以确保在处理一个中断时,不会将其他中断交给CPU。当然,操作系统这样做必须小心。禁用中断时间过长可能导致丢失中断,这(在技术上)是不好的。
- 操作系统还开发了许多复杂的加锁(locking)方案,以保护对内部数据结构的并发访问。这使得多个活动可以同时在内核中进行,特别适用于多处理器。
- 受限直接执行(limited direct execution)。基本思路很简单:就让你想运行的程序在CPU上运行,但首先确保设置好硬件,以便在没有操作系统帮助的情况下限制进程可以执行的操作。
- 宝宝防护(baby proofing)
- 通过类似的方式,OS首先(在启动时)设置陷阱处理程序并启动时钟中断,然后仅在受限模式下运行进程,以此为CPU提供“宝宝防护”。这样做,操作系统能确信进程可以高效运行,只在执行特权操作,或者当它们独占CPU时间过长并因此需要切换时,才需要操作系统干预。
5.补充笔记:通用寄存器和程序计数器
在计算机科学中,通用寄存器和程序计数器是*处理单元(CPU)内的关键组成部分。它们在程序执行过程中用于存储和处理信息。以下是通用寄存器和程序计数器的基本概念:
通用寄存器(General-Purpose Registers)
通用寄存器是CPU内部的一组小的数据存储位置,用于临时存储数据、地址、计算过程中的中间结果等。它们可以被编程用于多种任务,这也是“通用”这个名称的由来。在不同的架构中,通用寄存器的数量和用途可能有所不同,但通常它们包括:
- 累加器(Accumulator,A):用于算术和逻辑运算的结果。
- 基址寄存器(Base Register,B):通常用于地址计算,如存储数组的起始地址。
- 计数器(Counter,C):用于循环和迭代操作,如指示循环次数。
- 数据寄存器(Data Register,D):用于存储操作数和结果。
- 索引寄存器(Index Register,X):用于修改存储器地址,以便进行索引寻址模式。
这些寄存器可以在指令中直接寻址,使得数据的读写非常快速。在现代CPU架构中,如x86-64,还有更多的通用寄存器,如RAX, RBX, RCX, RDX等。
程序计数器(Program Counter, PC)
程序计数器,也称为指令指针(Instruction Pointer, IP),是一个特殊的寄存器,用于存储下一条将要执行的指令的地址。每次指令执行后,程序计数器会更新为下一条指令的地址,确保CPU能够按顺序执行程序中的指令。在某些操作,如跳转(jump)或调用(call)等操作时,程序计数器的值会被修改以指向一个新的地址。
程序计数器的作用至关重要,因为它控制着程序的流程。如果程序计数器的值不正确,可能会导致程序跳转到错误的地址执行,从而引发错误或崩溃。
区别和联系
通用寄存器和程序计数器的主要区别在于它们的用途:
- 通用寄存器:可以用于多种任务,包括但不限于数据存储、地址计算、临时存储计算结果等。
- 程序计数器:专用于追踪下一条执行指令的地址,控制程序的执行流程。
尽管它们的用途不同,但它们都是CPU内部对程序执行至关重要的部件。程序计数器和通用寄存器经常在指令执行过程中相互作用,例如,一个从内存加载数据到寄存器的指令会涉及到程序计数器(指出指令所在位置)和至少一个通用寄存器(存储加载的数据)。
6.补充笔记:如果一个进程在CPU上运行,这就意味着操作系统没有运行,如何理解?
这个说法并不准确。在现代操作系统中,多任务操作是通过时间片轮转或其他调度算法来实现的,这意味着CPU资源会在多个进程之间切换。当一个进程在CPU上运行时,它占用了CPU的资源,执行它的指令。然而,操作系统仍然控制着进程的调度,包括决定哪个进程接下来使用CPU,以及何时进行切换。
操作系统的一部分——特别是内核——始终在后台运行,即使在单个进程看似独占CPU时也是如此。内核负责管理所有底层的硬件资源,并提供系统调用接口供应用程序进行资源请求和管理。
在多任务操作系统中,操作系统的调度器会打断当前运行的进程,保存其状态(上下文切换),然后根据调度策略恢复另一个进程的状态并执行它。这个过程发生得如此频繁以至于用户通常感觉所有进程似乎都在同时运行,尽管在任何给定的时刻,单核CPU只能执行一个任务。
在多核或多处理器系统中,情况稍有不同。每个核或处理器可以独立地同时执行不同的进程或线程,但即便如此,每个核上的操作也仍然由操作系统来调度和管理。
总结来说,即使某个进程在CPU上运行,操作系统也仍然在运行,它在背后管理着整个系统的资源分配和进程调度。