操作系统导论——(一)操作系统介绍

本文详细探讨了操作系统如何通过虚拟化技术,包括虚拟化CPU、内存和多道程序,实现资源抽象,提高并发性和效率。从冯诺依曼模型机的背景出发,深入解析了虚拟化在现代计算机中的作用,以及个人计算机时代的操作系统发展,如Windows和Linux的演变。
摘要由CSDN通过智能技术生成

操作系统关键问题——虚拟化

        操作系统的职责,笼统地讲,是让计算机能够同时运行多个程序、让多个程序共享主存、让程序能够方便地与设备交互等等,即让系统更易于使用、更高效。而让操作系统更易于使用的核心问题是:操作系统如何实现资源虚拟化?如今的操作系统利用虚拟化的技术将物理资源(如处理器、内存、磁盘等其他I/O设备)转换为更通用、更易于使用的虚拟形式,因此有时可以将操作系统成为虚拟机(例如,JVM是一种执行java字节码文件(.class文件)的虚拟机,负责将字节码解释为对应平台的机器码指令,同一个Java程序可以借助不同的版本得JVM在不同的操作系统上运行,实现其跨平台性)。

        为了让用户可以告诉操作系统应该做什么,操作系统还提供一些API供用户调用,实际上也就是所谓的系统调用,用户编写的应用程序通过系统调用来利用虚拟机的功能。但并不能简单的把操作系统看作一组常用的函数库(虽然早期的操作系统只是一些库)。

冯诺依曼基本思想和冯诺依曼模型机

        这是一个前置知识,有助于理解后面的内容。冯诺依曼在研究EDVAC机(世界上第一台计算机ENIAC同时期的计算机,当时的计算机主要使用穿孔卡片和穿孔带存储程序)时提出“存储程序”的思想,该思想奠定了现代计算机的基本结构。其特点如下:

  1. 机器内部使用二进制表示数据,采用“存储程序”的工作方式,程序的指令和数据以同等地位存储在存储器中
  2. 计算机硬件系统由运算器、控制器、存储器、输入设备、输出设备5大部件组成。

存储程序的基本思想:将事先编制好的程序和其原始数据送入主存后才能执行,一旦程序被启动执行就无需操作人员干预,计算机会自动逐条执行指令,知道程序结束。如下图是冯诺依曼模型机的结构,奠定了现代计算机结构的基础。

054ed435a9bac977abd2c89f14f4856c.png

虚拟化CPU

        在计算机手工操作阶段和批处理阶段时,所有待运行的程序是串行执行——各作业按顺序执行,只有等一个程序执行结束之后,下个程序才能开始执行。再看如今的计算机,同一个程序可以同时有许多不同的实例(如果引入进程的概念则可以说多个进程实例可以运行同一个程序),即使只有一个处理器的情况下,多个实例依然可以同时运行,造成系统似乎拥有非常多的CPU的假象,这也就是所谓的虚拟化CPU。

      分时操作系统与上面提到的批处理系统不同,各程序实例的运行并非串行,它将处理器的运行分成很短的时间片,按时间片轮流把处理器分配给各作业使用,若某作业在操作系统给其分配的单个时间片内无法完成,则该作业会暂停运行,将处理器让给其他的作业使用,等待下一次处理器分配时间片给到自己时再运行,此时如果计算机的计算能力足够快(事实上对于人类来说确实足够快),作业的轮转也快到两次被分配时间片运行的时间间隔小到人类无法反应过来,这也就会造成每个用户都感觉自己独占计算机的假象。相比于批处理,分时操作系统的交互能力更强。

      足够细心则会发现,一次运行多个程序实例的能力似乎会引发一些新问题。例如,若个一个程序实例运行时修改了另一个暂停运行的程序的主存中的数据怎么办?(就好比数组越界)一个程序实例使用完一个时间片后,下一个时间片的处理器该分配给哪个程序?操作系统的许多不同地方采用了一些策略来回答这类问题。

虚拟化主存

      现代机器的主存就是一个字节数组,读取、写入或更新时都必须给出一个地址,指明操作的字节或字节序列。由于程序的指令和数据存放在主存,程序执行时需要一直访问主存,例如从储存中取出指令、从储存中取出指令的操作数、将程序执行的结果存入主存等等。

     C语言程序可以通过malloc函数分配一些内存。假设在一个程序中使用该函数分配内存,并在该片内存在存入其进程号,最后打印该片内存的首地址和该地址存储的数据。同时运行该程序的多个实例,我们会发现打印的地址是相同的,但地址中存储的数据却是不同的。这就好像每个正在运行的程序都有自己的私有内存,似乎不是与其他正在运行的程序嗯共享相同的物理内存。

      事实上,这正是操作系统虚拟化内存时发生的情况,每个进程访问自己私有的虚拟地址空间,操作系统以某种方式将该程序的虚拟地址空间映射到机器的物理内存上。也就是说,每个程序实例的虚拟地址空间是相同的,操作系统负责将各个程序实例的虚拟地址空间映射物理内存不同的区域,这种情况下虚拟地址对用户或者说应用程序是可见的,虚拟地址对应的物理地址对于用户是不可见的。

      每一个用户都有自己的地址空间,而且这个地址空间都是从0号地址开始(如下图),这十分方便用户编程,应用程序并不需要关心自己应该使用哪片实际的物理内存,直接使用虚拟地址空间即可,操作系统会负责将这片地址空间映射到物理内存,并对映射关系进行维护。

并发

        并发问题首先出现在操作系统本身,在上面关于虚拟化的讨论中,操作系统同时处理很多事情,它需要控制多个程序“同时”运行,这样做会导致一些问题。并发问题并不局限于操作系统本身,现代多线程程序也存在相同的问题。

        一个进程可以创建多个线程(引入线程的概念后,线程才是程序执行流的最小单元、处理机的分配单元),这些线程可以共享进程的地址空间,也就是可以共享进程的数据,线程只拥有少许运行时必不可少的资源,比如线程ID、程序计数器、寄存器和堆栈。

        假设程序中设置一个全局变量(即所有线程共享的变量)counter并初始化为0,然后设置一个worker函数,该函数执行三条指令:一条将counter的值从内存在读入寄存器,一条将寄存器中的值自增,最后一条将寄存器的值写回内存中的同一位置。在主函数中创建3个线程,这些线程都调用worker函数一次。我们预期的结果是counter最终为3。事实上,运行程序多次,我们会发现全局变量counter 的结果可能是3,也可能是2甚至1。这证明结果与指令如何执行有关,由于worker函数的三条指令,并不是以原子方式执行的,所以可能产生意想不到的结果。

        为什么三个线程均对counter自增一次,结果还会出现2或1:三个线程t1、t2、t3,假设接下来三个时间片依次分给了t1、t2和t3,而他们在自己的时间片内都只执行了第一条指令,此时三个线程都从主存中将counter的值取出并存入各自私有的寄存器,也就是说在这种执行顺序情况下,三个线程几乎“同时”读取了counter最初的值0,然后对其自增,三个线程的自增结果都是1,又执行函数中第三条指令存回主存,导致最后结果是1。如果三个线程依次执行完worker函数而非随机执行worker函数的三条指令,那么结果才会是3,也就是说t1取出counter、自增并存回主存后(此时为1),t2在从主存着取出counter并自增、存回主存(此时为2),最后t3执行worker函数的三条指令,这样的最终结果为3。

持久性

        在系统内存中数据容易丢失,因为例如DRAM这种设备通过晶体管中电压的高低来表示数据,一旦断电或系统崩溃,数据就会全部丢失,因此,我们需要硬件和软件来持久的存储数据。硬件以IO设备的形式出现,例如磁盘或固态硬盘等。操作系统中管理磁盘的软件通常称为文件系统,操作系统不会为每个应用程序向创建虚拟内存那样创建虚拟磁盘,相反它认为用户经常需要共享文件系统中的信息。例如用户通过编辑器创建一个文本文件并保存,之后使用打印机设备将该文本文件打印出来。这个过程中可以看到文件是如何在不同的进程之间共享的。

        操作系统为了将数据写入磁盘,文件系统需要做很多工作:首先确定新数据即将驻留在磁盘上的哪个位置,然后在文件系统所维护的各种结构中对其进行记录。这样做需要向底层IO设备发出请求。设备驱动程序的职责是让设备代表你执行某项复杂而细致的操作,这个过程需要深入了解低级别设备接口以及其确切的语义。幸运的是操作系统提供了一种通过系统调用来访问设备的简单方便的方法。

        关于如何访问设备、文件系统如何在设备上持久的管理数据,还有许多细节。出于性能方面的考虑,大多数文件系统会采取写延迟的策略。为处理写入期间系统崩溃的问题,大多数文件系统也会采取一些复杂的协议,此外,还有RAID和文件系统也需要了解。

操作系统简单历史

早期操作系统:只是一些库

        早期操作系统并没有做太多事情,只是一组常用函数库(就像现在的程序员封装的常用函数那样)。例如,不是让系统中,每个程序员都编写低级IO处理代码,而是让操作系统提供一个API,让开发人员更轻松。

不只是库:保护

        在发展过程中,操作系统在管理硬件方面扮演着更重要的角色。人们逐渐意识到代表操作系统运行的代码是特殊的,而不仅仅只是一个谁都可以调用的API。

        假如允许任何程序从磁盘上的任何地方读取数据或修改数据,那么隐私的概念就消失了。因此,将一个文件系统简单实现为一个库是没有意义的。因此,系统调用的概念诞生了——不是将操作系统例程看出一个库去提供,而添加了一些特殊的硬件指令和硬件状态(核心态和用户态,或者说管态和目态)。系统调用和过程调用的关键区别在于,系统调用将控制转移到操作系统中(硬件状态转为核心态),同时,提高硬件特权级别。用户应用程序以所谓的用户态运行,这意味着限制了应用程序对硬件的使用。例如,以用户太运行的程序,通常不能发起对磁盘的IO请求,不能访问任何物理内存页或在网络上发数据包。需要通过系统调用,将特权级别提升到内核态,在内核模式下,操作系统可以完全访问系统的硬件,诸如发起IO请求等等。

        当操作系统完成服务时,也就代表着系统调用完成,操作系统通过特殊的陷阱返回指令将控制权交还给用户,硬件状态变成用户态。或者理解为,对于一些“危险”的操作,应用程序需要请求操作系统去完成,也就是当系统进入核心态时,CPU所运行的指令序列是代表着操作系统去管理计算机,当操作系统完成操作时返回应用程序并变为用户态,使得CPU转而继续执行应用程序的指令序列。

多道程序时代

        操作系统的真正兴起在大主机计算时代之后,即小型计算机时代。由于希望更好地利用计算机资源,多道程序诞生。操作系统不是一次只控制一个作业的运行,而是将大量作业加载到内存中并在它们之间快速切换(类似分时系统的思想),从而提高CPU的利用率。这种切换是非常重要且非常有必要的,因为I/O设备很慢,如果一个程序需要进行IO操作,那么这个程序执行流将暂停,也就是说程序后续指令需要等待IO操作结束才能继续执行,在IO操作过程中CPU处于空闲状态,如果此时不切换其他程序,也就是白白浪费了CPU的性能和时间。由于多道程序的出现,内存保护、并发等问题变得重要,因为我们不希望一个程序能够访问另一个程序的内存。

摩登时代

        除了小型计算机之外,还有一种新型计算机,便宜、速度更快、更适用于大众——个人计算机(PC,personal computer)。在苹果公司早期的机器和IBM PC引领下,这种新计算机很快成为了计算的主导力量。小型计算机的操作系统也逐渐成熟,例如,从windows NT开始,微软的操作系统技术实现了巨大的飞跃。今天我们迎来了操作系统开发的鼎盛时期,我们有很多优秀的操作系统(例如大名鼎鼎的Linux、window等),并且他们仍在不断发展,让现代系统更加完善。

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值