操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

操作系统简介

嘿哈布尔! 我想提请您注意一系列文章——我认为是一本有趣的文学作品的翻译——OSTEP。 本材料非常深入地讨论了类 unix 操作系统的工作,即与进程、各种调度程序、内存和构成现代操作系统的其他类似组件一起工作。 你可以在这里看到所有材料的原件 这里. 请注意,翻译是非专业的(相当随意),但我希望我保留了一般意思。

关于这个主题的实验室工作可以在这里找到:

其他部分:

您也可以查看我的频道 电报 =)

让我们看看操作系统为用户提供的最基本的抽象:进程。 过程的定义非常简单——它是 运行程序。 程序本身是位于磁盘上的无生命的东西——它是一组指令,可能还有一些等待启动的静态数据。 操作系统获取这些字节并运行它们,将程序转换成有用的东西。
大多数情况下,用户希望同时运行多个程序,例如,您可以在笔记本电脑上运行浏览器、游戏、媒体播放器、文本编辑器等。 事实上,典型的系统可以同时运行数十或数百个进程。 这一事实使得系统更易于使用,您永远不必担心CPU是否空闲,您只需运行程序即可。

这就提出了一个问题:如何提供许多CPU的假象? 即使只有一个物理 CPU,操作系统如何才能创造出几乎无限数量的 CPU 的假象?

操作系统通过 CPU 虚拟化创造了这种错觉。 通过启动一个进程,然后停止它,启动另一个进程,依此类推,操作系统可以维持有许多虚拟 CPU 的假象,而实际上会有一个或多个物理处理器。 这种技术称为 CPU资源按时间划分。 该技术允许用户根据需要运行任意数量的并发进程。 此解决方案的成本是性能 - 因为如果 CPU 由多个进程共享,则每个进程的处理速度都会更慢。
为了实现CPU虚拟化,尤其是做好它,操作系统需要底层和高层的支持。 低水平支持称为 机制 是实现功能所需部分的低级方法或协议。 此类功能的一个示例是上下文切换,它使操作系统能够停止一个程序并在处理器上运行另一个程序。 所有现代操作系统都实现了这种时间划分。
这些机制之上是以“策略”的形式内置于操作系统中的一些逻辑。 政策 是操作系统一定的决策算法。 例如,此类策略决定应首先启动哪个程序(从命令列表中)。 例如,这个问题将通过一项名为 调度器(调度策略) 在选择解决方案时,会以以下数据为指导:启动历史记录(最后几分钟启动时间最长的程序)、该进程承载的负载(启动了哪些类型的程序)、性能指标(系统是否启动)针对交互式交互或吞吐量进行了优化)等等。

抽象:过程

操作系统执行的运行程序的抽象就是我们所说的 过程。 如前所述,进程只是在任何瞬时时间段内运行的程序。 一个程序,通过它我们可以从该程序在执行过程中访问或影响的各种系统资源中获取摘要信息。
要了解进程的组成部分,您需要了解系统的状态:程序在运行期间可以读取或更改什么。 在任何给定时间,您都需要了解系统的哪些元素对于程序的执行很重要。
该过程包含的系统状态的明显元素之一是 记忆。 指令位于内存中。 程序读取或写入的数据也位于内存中。 因此,进程可以寻址的内存(称为地址空间)是进程的一部分。
寄存器也是系统状态的一部分。 许多指令的目的是改变寄存器的值或读取其值,因此寄存器也成为进程操作的重要组成部分。
需要注意的是,机器状态也是由一些特殊寄存器组成的。 例如, IP——指令指针 — 指向程序当前正在执行的指令的指针。 还有 堆栈指针 以及与之相关的 帧指针,用于管理:函数参数、局部变量和返回地址。
最后,程序经常访问 ROM(只读存储器)。 此“I/O”(输入/输出)信息应包括进程当前打开的文件列表。

流程API

为了加深我们对该过程如何工作的理解,让我们研究应该包含在任何操作系统接口中的系统调用的示例。 这些 API 可以在任何操作系统上以一种或另一种形式使用。

创建 (创建):操作系统必须包含某种允许您创建新进程的方法。 当您在终端中输入命令或通过双击图标启动应用程序时,系统会向操作系统发送调用以创建新进程,然后启动指定的程序。
切除:既然有创建进程的接口,那么操作系统也应该提供强制删除进程的能力。 大多数程序在运行时会自然地自行启动和终止。 否则,用户希望能够杀死它们,因此停止该进程的界面将很有用。
稍等 (等待):有时等待进程完成很有用,因此提供了一些接口来提供等待的能力。
杂项控制 (各种控制):除了杀死和等待进程外,还有其他各种控制方式。 例如,大多数操作系统都提供冻结进程(停止其执行一段时间)然后恢复进程(继续执行)的功能
Status (state):有各种接口用于获取有关进程状态的一些信息,例如它已经运行了多长时间或当前处于什么状态。

操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

流程创建:详细信息

有趣的事情之一是程序到底是如何转化为进程的。 特别是操作系统如何选择并运行程序。 流程到底是如何创建的。
首先,操作系统必须将程序代码和静态数据加载到内存(进程地址空间)中。 程序通常以某种可执行格式位于磁盘或固态驱动器上。 因此,将程序和静态数据加载到内存的过程要求操作系统能够从磁盘读取这些字节并将它们放置在内存中的某个位置。

在早期的操作系统中,加载过程是急切完成的,这意味着整个代码在程序启动之前就已加载到内存中。 现代操作系统会惰性地执行此操作,即仅在程序执行期间需要时才加载代码或数据片段。

一旦代码和静态数据加载到操作系统内存中,在进程运行之前还需要完成一些事情。 必须为堆栈分配一定量的内存。 程序使用堆栈来存储局部变量、函数参数和返回地址。 操作系统分配该内存并将其提供给进程。 堆栈还可以分配一些参数,特别是它填充 main() 函数的参数,例如使用 argc 和 argv 数组。

操作系统也可能会分配一些内存给程序堆。 程序使用堆来显式请求动态分配的数据。 程序通过调用函数来请求这个空间 malloc() 并通过调用函数显式清除它 自由()。 堆是链接表、哈希表、树等数据结构所必需的。 起初,少量内存被分配给堆,但随着时间的推移,随着程序的运行,堆可以通过库 API 调用 malloc() 请求更多内存。 操作系统参与分配更多内存以帮助满足这些调用的过程。

操作系统还将执行初始化任务,特别是与 I/O 相关的初始化任务。 例如,在 UNIX 系统上,每个进程默认有 3 个打开的文件描述符,用于标准输入、输出和错误。 这些句柄允许程序读取来自终端的输入以及在屏幕上显示信息。

因此,通过将代码和静态数据加载到内存中、创建和初始化堆栈以及执行与执行 I/O 任务相关的其他工作,操作系统为进程的执行做好准备。 最后,还剩下最后一项任务:通过其入口点(称为 main() 函数)运行程序。 通过执行main()函数,操作系统将CPU控制权转移给新创建的进程,从而程序开始执行。

进程状态

现在我们对进程是什么以及它是如何创建有了一些了解,让我们列出它可以处于的进程状态。 在最简单的形式中,进程可以处于以下状态之一:
运行。 运行时,进程在处理器上运行。 这意味着指令正在执行。
各就各位。 在就绪状态下,进程已准备好运行,但由于某种原因操作系统没有在指定时间执行它。
受阻。 在阻塞状态下,进程执行一些操作,阻止其准备好执行,直到发生某些事件。 一个常见的例子是,当一个进程启动 IO 操作时,它会被阻塞,以便其他进程可以使用处理器。

操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

您可以以图表的形式想象这些状态。 正如我们在图中看到的,进程状态可以根据操作系统的判断在 RUNNING 和 READY 之间变化。 当一个进程的状态从READY变为RUNNING时,就意味着该进程已经被调度了。 在相反的方向 - 从布局中删除。 当一个进程变成BLOCKED的那一刻,比如我发起一个IO操作,操作系统会保持这个状态,直到某个事件发生,比如IO完成。 此时转换到 READY 状态,如果操作系统决定的话,可能会立即转换到 RUNNING 状态。
让我们看一个示例,了解两个进程如何经历这些状态。 首先,我们假设两个进程都在运行,并且每个进程仅使用 CPU。 在这种情况下,他们的状态将如下所示。

操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

在下面的示例中,第一个进程在运行一段时间后请求 IO 并进入 BLOCKED 状态,从而允许另一个进程运行(图 1.4)。 操作系统发现进程0没有使用CPU并启动进程1。当进程1运行时,IO完成,进程0的状态变为READY。 最后,进程 1 完成,完成后,进程 0 启动、执行并完成其工作。

操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

数据结构

操作系统本身是一个程序,就像任何其他程序一样,它有一些关键的数据结构来跟踪各种相关的信息。 为了跟踪每个进程的状态,操作系统将支持一些 进程列表 处于 READY 状态的所有进程以及一些用于跟踪当前正在运行的进程的附加信息。 此外,操作系统应该监视阻塞的进程。 IO完成后,操作系统必须唤醒所需的进程并将其置于准备运行的状态。

例如,操作系统必须保留处理器寄存器的状态。 当进程停止时,寄存器的状态存储在进程的地址空间中,当其操作继续时,寄存器的值被恢复,从而继续执行该进程。

除了就绪、阻塞、运行状态之外,还有一些其他状态。 有时,在创建时,进程可能处于 INIT 状态。 最后,当进程已经完成但其信息尚未清除时,可以将其置于 FINAL 状态。 在 UNIX 系统上,此状态称为 僵尸进程。 此状态对于父进程想要​​知道子进程的返回代码的情况很有用,例如,通常 0 表示成功,1 表示错误,但程序员可以发出额外的输出代码来表示不同的问题。 当父进程终止时,它会进行最终的系统调用,例如 wait(),以等待子进程终止并向操作系统发出信号,表明它可以清除与已终止进程相关的任何数据。

操作系统:三个简单的部分。 第 2 部分:抽象:过程(翻译)

讲座要点:

过程 ——操作系统中正在运行的程序的主要抽象。 在任何给定时间,进程都可以通过其状态来描述:其地址空间中的内存内容、处理器寄存器的内容(包括指令指针和堆栈指针)以及 IO 信息(例如正在读取或写入的打开文件)。
流程API 由程序可以对进程进行的调用组成。 通常这些是创建、删除或其他调用。
● 进程处于多种状态之一,包括运行、就绪、阻塞。 各种事件(例如调度、调度异常或等待)都可以将进程的状态从一种状态更改为另一种状态。
流程清单 包含系统中所有进程的信息。 其中的每个条目称为进程控制块,它实际上是一个包含有关特定进程的所有必要信息的结构。 

来源: habr.com

添加评论