操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

操作系统简介

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

关于这个主题的实验室工作可以在这里找到:
- 原来的: page.cs.wisc.edu/~remzi/OSTEP/Homework/homework.html
- 原来的: github.com/remzi-arpacidusseau/ostep-code
- 我个人的改编: github.com/bykvaadm/OS/tree/master/ostep

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

程序运行

程序运行时会发生什么? 一个正在运行的程序只做一件简单的事情——它执行指令。 每秒,处理器从 RAM 中检索数百万甚至数十亿条指令,然后对它们进行解码(例如,它识别这些指令属于什么类型)并执行它们。 这可能是将两个数字相加、访问内存、检查条件、跳转到函数等。 执行一条指令后,处理器继续执行另一条指令。 一条条指令接一条指令执行,直到程序结束。
这个例子自然被认为是简化的——事实上,为了加速处理器,现代硬件允许你不按顺序执行指令、计算可能的结果、同时执行指令,以及类似的技巧。

冯诺依曼计算模型

我们描述的简化工作形式类似于冯诺依曼计算模型。 冯诺依曼是计算机系统的先驱之一,他也是博弈论的创始人之一. 在程序运行的过程中,会发生一堆其他的事件,很多其他的进程和第三方逻辑在工作,其主要目的是为了简化系统的启动、运维。
有一组软件负责使程序易于运行(甚至允许多个程序同时运行),允许程序共享相同的内存,并与不同的设备进行通信。 这样的一套软件(软件)本质上称为操作系统,其任务包括监控系统是否正确有效地工作,以及确保该系统易于管理。

操作系统

操作系统,简称OS,是一组相互关联的程序,旨在管理计算机资源和组织用户与计算机的交互。.
操作系统首先通过最重要的技术实现其有效性——技术 虚拟化. 操作系统与物理资源(处理器、内存、磁盘等)交互,并将其转化为更通用、更强大、更易于使用的自身形式。 因此,为了大体的理解,可以非常粗略地将操作系统比作虚拟机。
为了允许用户向操作系统下达命令,从而使用虚拟机的能力(如运行程序、分配内存、访问文件等),操作系统提供了一些接口,称为 API (应用程序编程接口),您可以对其进行调用(调用)。 一个典型的操作系统允许进行数百个系统调用。
最后,由于虚拟化允许多个程序同时运行(从而共享CPU),同时访问它们的指令和数据(从而共享内存),访问磁盘(从而共享I/O设备),操作系统也被称为资源管理器。 每个处理器、磁盘和内存都是系统的一种资源,因此操作系统的角色之一变成了管理这些资源的任务,高效、诚实地进行管理,反之亦然,这取决于操作系统的任务被设计。

CPU虚拟化

考虑以下程序:
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

它不执行任何特殊操作,事实上,它所做的只是调用一个函数 (),其任务是循环时间检查,一秒后返回。 因此,它无限期地重复用户作为参数传递的字符串。
让我们运行这个程序并将字符“A”作为参数传递给它。 结果并不是特别有趣——系统只是执行一个周期性显示字符“A”的程序。
现在让我们试试当同一个程序的许多实例运行时的选项,但输出不同的字母以使其更清楚。 在这种情况下,结果会有些不同。 尽管我们只有一个处理器,但程序是同时执行的。 它是如何发生的? 但事实证明,操作系统并非没有硬件功能的帮助,而是产生了一种错觉。 系统具有多个虚拟处理器的错觉,将单个物理处理器变成理论上无限的数量,从而允许看似程序同时运行。 这种错觉被称为 CPU虚拟化.
这张图提出了很多问题,比如,如果要同时运行几个程序,启动哪个? 操作系统的“策略”负责这个问题。 策略在操作系统中有很多地方用到,回答这样的问题,是操作系统实现的基本机制。 因此操作系统的角色是资源管理器。

内存虚拟化

现在让我们看看内存。 现代系统中内存的物理模型表示为字节数组。. 要从内存中读取,您需要指定 小区地址访问它。 要写入或更新数据,您还必须指定数据和写入数据的单元格地址。
在程序执行期间不断访问内存。 程序将其整个数据结构存储在内存中,并通过执行各种指令来访问它。 与此同时,这些指令也存储在内存中,因此对于下一条指令的每个请求也会访问它。

malloc() 调用

考虑下面的程序,它使用调用分配一个内存区域 malloc() (https://youtu.be/jnlKRnoT1m0):

操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

该程序做了几件事。 首先,它分配一些内存(第 7 行),然后打印分配的单元格的地址(第 9 行),将零写入分配内存的第一个槽。 接下来,程序进入一个循环,在该循环中它递增存储在内存中“p”变量地址处的值。 它还打印自己的进程 ID。 每个正在运行的进程的进程ID都是唯一的. 启动多个副本后,我们会偶然发现一个有趣的结果:在第一种情况下,如果您什么都不做,只是运行多个副本,那么地址将会不同。 但这不属于我们的理论! 正确,因为现代发行版默认启用内存随机化。 如果它被禁用,我们会得到预期的结果——两个同时运行的程序的内存地址将匹配。

操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

结果表明,两个独立的程序使用自己的私有地址空间工作,而这些地址空间又由操作系统映射到物理内存中. 因此,在一个程序中使用内存地址不会以任何方式影响其他程序,而且在每个程序看来,它都有自己的一块物理内存,完全是给它的。 然而,实际情况是物理内存是由操作系统管理的共享资源。

一致性

操作系统中的另一个重要主题是 - 一致性. 当谈论在同一程序中同时处理许多事情时可能发生的系统问题时使用该术语。 甚至在操作系统本身内部也会出现一致性问题。 在前面的内存和处理器虚拟化示例中,我们意识到操作系统同时管理许多事情——它启动第一个进程,然后启动第二个,依此类推。 事实证明,这种行为会导致一些问题。 因此,例如,现代多线程程序会遇到这样的困难。

考虑以下程序:

操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

main 函数中的程序使用调用创建了两个线程 pthread_create(). 在这个例子中,线程可以被认为是与其他函数一起在同一内存空间中运行的函数,显然不止一个函数同时运行。 在这个例子中,每个线程启动并执行函数 worker() 反过来简单地增加变量,.

让我们用参数 1000 运行这个程序。您可能已经猜到,结果应该是 2000,因为每个线程都会将变量递增 1000 次。 然而,一切并没有那么简单。 让我们尝试以一个数量级的重复次数运行该程序。

操作系统:三个简单的部分。 第 1 部分:介绍(翻译)

通过输入一个数字,例如 100000,我们希望看到输出为数字 200000。但是,如果我们多次运行 100000 这个数字,我们不仅不会看到正确答案,还会得到不同的错误答案。 答案在于,要增加数字,需要三个操作——从内存中提取数字,递增,然后将数字写回。 由于所有这些指令都不是自动执行的(所有同时执行),因此可能会发生这种奇怪的事情。 这个问题在编程中叫做 竞争条件. 当未知力量在未知时刻影响您的任何操作的性能时。

来源: habr.com

添加评论