[翻译] Operating System-There Easy Pieces 4.进程的抽象

进程的抽象

在本文中,我们将讨论操作系统为用户提供的最基本的抽象之一:进程。 非正式地定义一个进程非常简单:它是一个正在运行的程序。程序本身是一个没有生命的东西:它只是存储在磁盘上的一堆指令(也许还有一些静态数据),等待开始行动。 操作系统采用这些字节并使它们运行,将程序转换为有用的东西。

事实证明,人们经常想要同时运行多个程序; 例如,您可能希望在笔记本或者PC上同时运行Web浏览器,邮件程序,游戏,音乐播放器等应用。 事实上,典型的系统似乎可以同时运行数十甚至数百个进程。 这样做使系统易于使用,不需要关心CPU是否可用,只需要运行程序就行。 因此我们存在的挑战是:

问题的关键:

如何提供许多CPUS的错觉?

虽然只有少数物理CPU可用,但操作系统如何提供几乎无穷无尽的CPU供应的错觉?

 

操作系统通过虚拟化CPU来创建这种错觉。通过运行一个进程,然后停止它并运行另一个进程,这样一直轮换执行,操作系统可以制造出存在许多虚拟CPU的错觉。而实际上只有一个物理CPU(或少数几个)。这种基本技术称为CPU的时间共享(时间片),允许用户运行任意数量的并发进程。潜在的成本是性能损耗,因为如果必须共享CPU,每个进程都会运行得更慢。

为了更好的实现CPU的虚拟化,操作系统既需要一些低级措施,也需要一些高级策略。我们称这些低级措施为“机制”。机制是实现所需功能的低级功能的方法或协议。例如,我们将在下面学习如何实现上下文切换,这使操作系统能够停止运行一个程序并在给定的CPU上开始运行另一个程序。所有现代操作系统都采用这种分时机制。

除了这些机制之外,还以策略的形式存在操作系统中的一些智能算法。策略是在OS中做出某种决定的算法。例如,给定在CPU上运行的许多可能的程序,OS应该运行哪个程序?操作系统中的调度策略将做出此决定,可能使用历史信息(例如,哪个程序在最后一分钟运行得更多?),工作负载知识(例如,运行的程序类型)和性能指标(例如,系统是否优化交互性能或吞吐量?)来做出决定。

 

提示:使用时间共享(和空间共享)

时间共享是操作系统共享资源的最基本技术之一。 通过允许资源被一个实体使用一段时间,然后稍微使用另一个实体,等等,所讨论的资源(例如,CPU或网络链接)可以被许多人共享。 时间共享的自然对应物是空间共享,其中资源在希望使用它的人之间被划分(在空间中)。 例如,磁盘空间自然是空间共享资源,因为一旦将块分配给文件,在用户删除它之前不可能将其分配给另一个文件。

 

4.1 进程的抽象

运行的程序在操作系统中的抽象我们称之为进程。如上所述,一个进程就是一个正在运行的程序。在任何时刻,我们都可以通过对其在执行过程中访问或影响的系统的不同部分进行清点来总结一个过程。

为了理解流进程的构成,我们必须了解其机器状态:程序在运行时可以读取或更新的内容。在任何给定时间,机器的哪些部分对执行该程序很重要?

包含进程的机器状态的一个明显组成部分是其存储器。指令存在内存中,运行程序读写的数据也在内存中。因此,进程可以寻址的内存(称为其地址空间)是进程的一部分。

进程的机器状态的一部分是寄存器。许多指令明确地读取或更新寄存器,因此它们对于执行过程很重要。

请注意,有一些特殊的寄存器构成了这种机器状态的一部分。例如,程序计数器(PC)(有时称为指令指针或IP)告诉我们当前正在执行哪个程序指令。类似地,堆栈指针和相关的帧指针用于管理函数参数,局部变量和返回地址的堆栈。

最后,程序通常也访问持久存储设备。此类I / O信息可能包括进程当前打开的文件列表。

 

提示:单独的政策和机制

在许多操作系统中,常见的设计范式是将高级策略与其低级机制分离[L + 75]。 您可以将机制视为提供有关系统问题的答案; 例如,操作系统如何执行上下文切换? 该政策为哪个问题提供了答案; 例如,操作系统现在应该运行哪个进程? 将两者分开可以很容易地改变策略,而不必重新考虑该机制,因此是一种模块形式,一般的软件设计原则。

 

4.2 进程API

虽然我们将实际流程API的讨论推迟到后续章节,但在这里我们先了解一些操作系统的最基本的接口。这些API以某种形式可用于任何现代操作系统。

 

creat:一个操作系统必须包含一些创建新进程的方法。在shell中键入命令时,或双击应用程序图标,调用操作系统以创建一个新进程来运行您指定的程序。

destory由于存在用于创建流程的接口,因此系统还提供了强制销毁进程的接口。当然,许多进程都会运行完成后自行退出。但是,当他们不这样做时,用户可能希望杀死他们,因此停止失控进程的接口非常有用。

wait:有时等待进程停止运行是有用的。因此经常提供某种等待接口。

Miscellaneous Control:除了杀死或等待进程之外,有时还有其他可能的控制。例如,大多数操作系统提供某种方法来暂停进程(阻止它运行一段时间)然后恢复它(继续运行)。

Status:通常还有接口来获取有关进程的状态信息,例如运行的时间或状态。

[翻译] Operating System-There Easy Pieces 4.进程的抽象_第1张图片

 

4.3 进程创建的一些细节

我们应该揭露的一个谜团是一个程序是如何变成进程的?具体来说,操作系统如何启动并运行程序?进程创建过程实际上如何运作?

操作系统运行程序必须做的第一件事是将其代码和任何静态数据(例如,初始化的变量)加载到进程的地址空间中。程序最初以某种可执行格式驻留在磁盘上(现在大都是基于闪存的SSD)。因此,将程序和静态数据加载到内存中的过程需要操作系统从磁盘读取这些字节并将它们放在内存中(如图4.1所示)。

在早期(或简单)操作系统中,加载过程是在运行程序之前完成的,即一次完成。现代操作系统懒惰地执行该过程,即,仅在程序执行期间需要加载代码或数据。要真正了解代码和数据的延迟加载是如何工作的,您必须更多地了解分页和交换的机制,我们将在讨论内存虚拟化时讨论这些主题。现在,请记住,在运行任何操作之前,操作系统显然必须做一些工作才能将重要的程序位从磁盘放入内存。

 

一旦将代码和静态数据加载到内存中,操作系统在运行该进程之前还需要执行一些其他操作。必须为程序的运行时堆栈(或只是堆栈)分配一些内存。正如您可能已经知道的那样,C程序将堆栈用于存放局部变量,函数参数和返回地址。操作系统分配此内存并将其提供给进程。操作系统也可能会通过参数初始化堆栈。具体来说就是,堆栈中会保存main()函数的参数,即argc和argv数组。

操作系统还可以为程序的堆创建一些初始内存。在C程序中,堆用于显式请求的动态分配数据。程序通过调用malloc()来请求这样的空间,并通过调用free()显式释放它。数据结构需要堆,例如链表,哈希表,树和其他有趣的数据结构。堆最初会很小;当程序运行,并通过malloc()库API请求更多内存时,操作系统可能会进入内核并为进程分配更多内存以帮助满足此类调用。操作系统还会执行一些其他初始化任务,尤其是与输入/输出(I / O)相关的任务。例如,在UNIX系统中,默认情况下每个进程都有三个打开的​​文件描述符,用于标准输入,输出和错误。这些描述符使程序可以轻松地从终端读取输入以及将输出打印到屏幕。我们将在本书的第三部分中详细介绍有关持久性的I / O,文件描述符等内容。

通过将代码和静态数据加载到内存中,通过创建和初始化堆栈,以及通过执行与I / O设置相关的其他工作,操作系统现在(最终)为程序执行设置了做好了铺垫。然后它还有一个最后的任务:跳转到在入口点去运行程序,即main()。通过跳转到main()例程(通过我们将在下一章讨论的专用机制),OS将CPU的控制权转移到新创建的进程,从而程序开始执行。

 

4.4 进程状态

现在我们已经知道一个进程是什么(虽然我们将继续改进这个概念),并且(大致)知道如何创建它,让我们谈谈一个过进程在给定时间可以处于的不同状态。 在早期的计算机系统中出现了一个进程可能处于这些状态之一的概念。 在简化视图中,进程可以处于以下三种状态之一:

•正在运行(running):在运行状态下,进程正在处理器上运行。 这意味着它正在执行指令。

•就绪(ready):在就绪状态下,一个进程已准备好运行,但出于某种原因,操作系统不选择在此时刻运行它。

•阻塞(blocked):在阻塞状态下,进程已执行某种操作,使其在其他事件发生之前无法继续运行。 一个常见示例:当进程向磁盘发起I / O请求时,它会被阻塞,然后其他一些进程可以使用该处理器。

[翻译] Operating System-There Easy Pieces 4.进程的抽象_第2张图片

Figure 4.2: Process: State Transitions

如果我们将这些状态映射到图表,我们将到达图4.2中的图表。 正如您在图中看到的那样,可以根据操作系统的判断在准备状态和运行状态之间移动进程。 从准备运行到运行意味着该过程已被计划; 从运行转移到准备就绪意味着该过程已被取消预定。 一旦进程被阻塞(例如,通过启动I / O操作),OS将保持这样直到某些事件发生(例如,I / O完成); 此时,进程再次进入就绪状态(如果OS决定,可能会立即再次运行)。

 

4.5 数据结构

操作系统是一个程序,与任何程序一样,它有一些跟踪各种相关信息的关键数据构。例如,为了跟踪每个进程的状态,操作系统可能会为所有准备好的进程保留某种进程表,以及跟踪当前正在运行的进程的一些附加信息。操作系统还必须以某种方式跟踪被阻止的进程。当I / O事件完成时,操作系统应确保唤醒正确的进程并准备好再次运行。

图4.3显示了操作系统需要跟踪xv6内核[CK + 08]中每个进程的信息类型。类似的过程结构存在于“真实”操作系统中,例如Linux,Mac OS X或Windows。查看它们,看看它们有多复杂。

从图中,您可以看到操作系统跟踪流程的几个重要信息。对于已停止的进程,寄存器区域(PCB中的字段)将保持其寄存器的内容。当进程停止时,其寄存器将保存到该存储单元。通过恢复这些寄存器(即将它们的值放回实际的物理寄存器中),操作系统可以恢复运行该过程。我们将在以后的章节中详细了解这种称为上下文切换的技术。

[翻译] Operating System-There Easy Pieces 4.进程的抽象_第3张图片

Figure 4.3: The xv6 Proc Structure

您还可以从图中看到,除了运行,准备和阻塞之外,还有一些其他状态可以进入。 有时,系统中会有处于刚创建的初始状态的进程。 此外,可以将进程置于已退出但尚未清除的最终状态(在基于UNIX的系统中,这称为僵尸状态)。 这个最终状态可能很有用,因为它允许其他进程(通常是创建进程的父进程)检查进程的返回代码,看看它是否已成功执行成功。(通常在基于UNIX的系统中,程序执行成功会返回零,否则非零)。 完成后,父进程将进行最后一次调用(例如,wait())以等待子进程完成,并向操作系统表明它可以清理任何涉及该进程的相关数据结构 - 灭绝过程。


 

ASIDE:数据结构 - 进程列表

操作系统充满了各种重要的数据结构,我们将在这些对应的章节讨论。 进程列表是第一个这样的结构,而且是其中比较简单的一个。当然任何能够同时运行多个程序的操作系统都会有类似于这种结构的东西,以便跟踪系统中所有正在运行的程序。 有时人们会将存储过程信息的个体结构称为过程控制块(PCB),这是一种讨论包含每个进程信息的C结构的奇特方式。


 

4.6 总结

我们介绍了操作系统最基本的抽象:进程。 它被简单地视为一个正在运行的程序。 考虑到这一概念性观点,我们现在将继续讨论实质性过程:实施进程所需的低层机制,以及以智能方式安排进程所需的更高层次的策略。 通过结合机制和策略,我们将建立对操作系统如何虚拟化CPU的理解。

 

你可能感兴趣的:([翻译] Operating System-There Easy Pieces 4.进程的抽象)