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

操作系统介绍


哈Ha! 我想提请您注意一系列文章-我认为是OSTEP的有趣文献的翻译。 本文相当深入地讨论了类Unix操作系统的工作,即处理组成现代OS的进程,各种调度程序,内存和其他类似组件。 您可以在这里看到所有材料的原始材料。 请注意,翻译是非专业地进行的(相当自由),但是我希望我保留了一般的含义。

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


其他部分:

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

程序工作


程序运行时会发生什么? 运行程序可以做一件事-它执行指令。 处理器每秒从RAM中提取数百万甚至数十亿条指令,然后对它们进行解码(例如,识别这些指令属于哪种类型)并执行。 这可以是两个数字的加法,访问内存,检查条件,切换到功能等等。 完成一条指令后,处理器将继续执行另一条指令。 因此,逐条指令执行它们,直到程序完成。

该示例自然被认为是简化的-实际上,为了加快处理器速度,现代硬件使您可以依次执行指令,计算可能的结果,同时遵循指令和类似技巧。

冯·诺依曼计算模型


我们描述的简化工作形式类似于冯·诺依曼的计算模型。 冯·诺依曼(Von Neumann)是计算机系统的先驱者之一;他也是博弈论的作者之一 。 程序运行时,还会发生许多其他事件,许多其他进程和第三方逻辑会工作,其主要目的是简化系统的启动,操作和维护。

有一套软件负责简化程序的运行(甚至允许您同时运行多个程序),它允许程序共享相同的内存以及与不同的设备进行交互。 这样的一组软件(软件)在本质上被称为操作系统,其任务包括监视系统正确,有效地工作以及确保对该系统的易于管理。

作业系统


操作系统(简称OS)是一组相互连接的程序,旨在管理计算机资源和组织用户与计算机的交互

操作系统主要通过最重要的技术- 虚拟化技术来实现其有效性。 OS与物理资源(处理器,内存,磁盘等)进行交互,并将其转换为更通用,更强大且更易于使用的自身形式。 因此,为了大致了解,您可以将操作系统与虚拟机进行非常粗略的比较。

为了允许用户向操作系统提供指令,从而使用虚拟机的功能(例如:启动程序,分配内存,访问文件等),操作系统提供了一些称为API (应用程序编程接口)的接口,您可以拨打电话。 典型的操作系统可以进行数百个系统调用。

最后,由于虚拟化允许许多程序运行(因此共享CPU),并同时获得对它们的指令和数据的访问权(从而共享内存),以及访问磁盘(从而共享I / O设备) ),操作系统也称为资源管理器。 每个处理器,磁盘和内存都是系统的资源,因此操作系统的角色之一成为管理这些资源,有效,诚实或相反地取决于设计此操作系统的任务来管理这些资源的任务。

CPU虚拟化


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

图片

它不执行任何特殊操作,实际上它所做的只是调用spin ()函数,其任务是循环时间并在经过一秒钟后返回。 因此,它会无休止地重复用户作为参数传递的字符串。

运行该程序,并将符号“ A”作为参数传递给它。 结果不是很有趣-系统仅运行一个程序,该程序定期显示符号“ A”。

现在,当同一程序的许多实例正在运行但显示不同的字母时,让我们尝试一下该选项,以便于理解。 在这种情况下,结果将略有不同。 尽管我们只有一个处理器,但该程序会同时运行。 这是怎么发生的? 但是事实证明,并非没有硬件功能的帮助,操作系统会产生一种幻觉。 错觉是系统中有多个虚拟处理器,从而将一个物理处理器转变为理论上无限的数量,从而使程序看起来可以同时执行。 这种错觉被称为CPU虚拟化

这种情况引起了很多问题,例如,如果要同时启动几个程序,将启动哪个程序? 操作系统的“策略”负责这个问题。 策略在操作系统的许多地方都使用,并回答类似的问题,也是操作系统实现的基本机制。 因此,操作系统作为资源管理器的角色。

内存虚拟化


现在让我们看一下内存。 现代系统中的物理内存模型表示为字节数组 。 要从内存中读取,必须指定单元地址才能访问它。 要记录或更新数据,还必须指定数据以及将数据写入单元的地址。

在程序运行期间,存储器访问不断发生。 程序将其整个数据结构存储在内存中,并按照各种指令进行访问。 同时,指令也存储在内存中,因此对下一条指令的每个请求都可以对其进行访问。

调用malloc()


考虑以下程序,该程序使用malloc()调用(https://youtu.be/jnlKRnoT1m0)分配内存区域:

图片

该程序可以做一些事情。 首先,它分配一定数量的内存(第7行),然后显示所选单元的地址(第9行),将零写入已分配内存的第一个插槽。 接下来,程序进入一个循环,在该循环中,程序会递增变量“ p”中地址处内存中记录的值。 它还显示自身的进程标识符。 进程ID对于每个正在运行的进程都是唯一的 。 启动了多个副本后,我们会遇到一个有趣的结果:在第一种情况下,如果不执行任何操作而仅启动多个副本,则地址将有所不同。 但这不属于我们的理论范围! 的确如此,因为在现代发行版中,默认情况下会启用内存随机化功能。 如果将其关闭,我们将得到预期的结果-两个同时运行的程序的内存地址将重合。

图片

结果,事实证明两个独立的程序使用它们自己的专用地址空间,这些地址又由操作系统显示在物理内存中 。 因此,在一个程序中使用内存地址不会以任何方式影响其他程序,并且在每个程序中它似乎都有自己的物理内存,这完全由其支配。 但是,现实情况是物理内存是由操作系统管理的共享资源。

连贯性


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

考虑以下程序:

图片

主函数中的程序使用Pthread_create()调用创建两个线程。 在此示例中,线程可以被认为是在其他功能之后的同一内存空间中运行的功能,并且同时启动的功能数量显然不止一个。 在此示例中,每个线程启动并执行worker()函数, 函数依次简单地递增变量。

使用参数1000运行该程序。您可能已经猜到了,结果应该是2000,因为每个线程将变量递增1000次。 但是,一切并非如此简单。 让我们尝试以重复次数多一个数量级的方式运行程序。

图片

通过输入一个数字(例如100000),我们期望在输出上看到200000,但是,多次运行该数字100000,我们不仅看不到正确答案,而且会得到不同的错误答案。 答案在于以下事实:要增加数量,需要执行三个操作-从内存中提取数量,递增,然后再写回。 由于并非自动执行所有这些指令(全部同时执行),因此可能会发生此类奇怪的事情。 这个问题称为竞争条件编程- 竞争条件 。 如果未知时刻的未知力量会影响您的任何操作的性能。

Source: https://habr.com/ru/post/zh-CN446340/


All Articles