小明学C++第五篇:操作系统

来源:互联网 发布:2016男女对唱新歌网络 编辑:程序博客网 时间:2024/05/22 12:47

本篇博客重点在于文件系统,中断、进程的概念,以及整个存储系统的实现

背景

前面讲到小明编写了一个多边形面积计算的程序,小明将它编译成二进制可执行代码保存在计算机中。然后小明双击该文件并运行,期间小明输入了几个顶点坐标后,最终控制台显示了正确的结果。那么这期间又发生了什么呢?

文件的存储

讲到文件的存储,就涉及到文件系统。
下面一段句子是微软对文件系统的描述:

A file system is a part of the operating system that determines how files are named, stored, and organized on a volume. A file system manages files and folders, and the information needed to locate and access these items by local and remote users.

windows系统使用过FAT文件系统,NTFS文件系统。
FAT是早期使用的文件系统,它比较简单,适合我们学习文件系统的组织方法。

我们知道,在计算机中,用来存储的有内存和磁盘,但是内存容量有限,而且断电后内容就消失了,因此,文件是保存在磁盘中的。而磁盘的空间分配是以盘块为单位的。因此,一个文件就可能包含多个盘块,而一个盘块最多只能属于一个文件。那么如何组织这些盘块,使得访问文件内容的时候能够较快进行,就成为最重要的问题。

FAT文件系统就是用链接的方式,来描述一个文件所包含的盘块的。它的核心在于一个FAT(file allocation table)表:
这里写图片描述
上图中,左边的圆柱体是磁盘,右上角是一个文件目录内容的描述,右下角就是一个FAT文件分配表。
理论上,磁盘上一共有多少个盘块,FAT表就有多少项。FAT表的每一项都有一个指针指向下一个盘块,如果指针值为-1表示文件的结尾。

文件moo的定位
现在我们来下是如何找到moo的文件内容的。首先我们找到moo所在的目录,发现moo文件的起始盘块是5,就可以读取盘块5的内容,然后盘块5的指针指向12,盘块12指向1,盘块1的内容为EOF。这表明,文件moo的内容依次存放在5、12、1三个盘块中。

一个文件系统还包括创建新文件、删除文件等操作,但是本篇博客的目的在于了解文件系统,感兴趣的同学可以自行深入了解。

运行中的程序:进程

编译完程序后,双击二进制可执行文件,得到如下界面:
这里写图片描述
上图的黑色窗口是程序的控制台,而背后的窗口是任务管理器的窗口。我们可以看到在进程一栏,包含了一个名字叫做多边形面积求解的进程。那么什么是进程呢?
进程又是如何创建的呢?
什么是进程
一个进程包含了程序代码、数据、以及进程控制块PCB(process control block);进程可以理解为是运行中的程序。为了区分不同的进程,操作系统会给每个进程一个唯一的进程ID(PID),而进程控制块就包含了PID,进程创建时间、进程创建者、目前使用的内存等信息;总的来说,进程控制块就是为了维护一个进程的必要信息。
进程的创建
进程的创建是一个较为复杂的过程,包含了用户态到内核态的转换,本人由于水平有限也无法讲解详细的流程。我只说一下大概的思路。进程创建的主要目标就是构造出PCB该有的资源。当小明双击可执行文件时,操作系统将程序代码以及数据从磁盘装载到内存上,根据程序的要求给予一些资源(比如说存储空间),但是此时唯独没有CPU,还不能运行。
现在这个进程真是万事具备,只欠CPU啊。
可是在操作系统眼中,远不止你一个进程需要运行,还有很多进程也在等待运行。因此操作系统需要运行特定的进程调度算法(一般都是多级反馈时间片轮转调度算法),来选择哪个进程可以获取CPU资源。进程一旦得到CPU资源,就可以执行了,这时进程的状态由就绪态变为执行态。执行到一段时间,需要等待用户从键盘输入数据,这时进程又从执行态变为阻塞状态,阻塞的原因是等待键盘的输入。
这时,CPU又拿去给别的进程使用了。

等待输入的程序:中断

两种通知方式:轮询vs中断
为了确定用户是否在键盘输入了数据,可以采用两种方式。第一种是轮询,轮询的意思就是CPU一直在访问键盘的状态寄存器,直到状态寄存器为1,用户输入数据为止。这个时候CPU也不去干别的事。而中断不一样,中断是在等待用户输入的时候,CPU转向去做别的事情,直到用户完成了输入,键盘向操作系统发送中断请求,这时CPU才处理键盘输入的事情。
通过上面的描述,已经高下立见了。中断胜出,理由是中断消耗更少的CPU资源。
中断步骤:
(1)I/O设备发出中断信号
当用户从键盘输入字符并按下enter键后,I/O设备将KBSR状态寄存器的ready位置1,表示“请求服务”;同时还需要在状态寄存器的中断使能标志(Interrupt Enable)置1,设备通过这种方法,就发出了中断信号。
(2)中断检测
在第四篇讲流水线的时候,我们知道一个指令的执行包含了五个步骤:取值、译码、执行、访存、回写。那么,提供了中断的功能后,就需要在回写这个步骤的同时对中断信号(INT)进行检测,如果INT信号有效,就需要对中断做出回应,响应中断或者继续顺序执行下一条指令。
(3)中断判优
当有很多设备发出中断信号,或者运行中的程序与中断信号进行竞争时,就需要中断判优。计算机中给每一个程序都定了优先级,只有当中断信号的优先级高于运行中的程序的优先级或者高于其他中断信号的优先级时,中断信号才会得到响应。
(4)中断响应
要响应中断,首先需要保存被中断程序的状态,包括PC指针以及条件码等信息;以便在中断过后恢复处理器的状态。
然后通过中断矢量或者程序查询的方式查找出中断服务程序的地址。
查到地址之后,就执行中断服务程序,这个程序的功能是将键盘数据寄存器的内容拷贝到内存的某个位置。
最后,使用中断返回指令RTI将PC值和条件码等值利用栈复原,被中断程序进行执行下去,就好像什么都没有发生一样。
这是,多边形面积计算的进程得到了中断响应以后,就从阻塞态变为就绪态,排队等待CPU继续运行下去。

存储器的金字塔结构

接下来就是这篇博客的重头戏了。
经过时光的洗涤,在计算机中,存储器已经变成了计算机的中心。
那么存储器的结构是怎样的?访问存储器需要经过哪些过程?

都说工业界是最好的战场,各大商家为了让自己的电脑又快有便宜,不禁使出浑身解数,最终想了一个狠招-金字塔模型。
我们先来看下存储器层次结构示例图
这里写图片描述
你看,上图像不像一个金字塔?上图的面积可以表示容量大小,越往上,面积越小,容量越小,但是价格更贵。
越往下价格便宜,所以容量更大。
正所谓上层经济决定上层建筑。
上述金字塔可以分为两个层次:cache-主存、主存-辅存。
cache-主存这一层主要是为了解决存储器速度跟不上CPU速度的问题。而主存-辅存主要解决存储系统容量不够的问题。
有了这个金字塔结构,存储器变得又快又大。
又快又大的原理
为什么有了金字塔结构就能够变得又快又大?
这源于计算机一个很重要的哲学观-程序只会跟它周围的环境发生作用,在访问一个数据之后,我将这个数据周围的数据也调入上一层存储器,这样下次访问数据能够快速访问了。这就是局部性原理

访问存储器的过程

复杂的存储结构,也对应着复杂的访问过程。首次访问数据,可能要涉及页表、TLB、cache、内存、缺页中断等问题。下面我们就来着重讲下这其中的过程。

操作系统的诡计:虚拟的用户空间
操作系统本来没有太大的存储空间,但是为了让磁盘空间成为内存的延伸、用户更加专心统一地编程,操作系统利用自己的能力构造出了一个虚拟存储空间。
这里写图片描述
如上图所示,左上角和右上角都是两个进程,它们都需要一定的存储空间去运行,也就是说虚拟内存只有映射到实际的物理内存上才能运行。但是在每一个时刻,进程是使用的内存空间是比较小的;因此,在请求分页机制中,将当前正在使用的虚拟块与物理块的映射关系写入一个页表当中。以后每次进程想要访问存储器,都用页表来查找对应的物理地址,最后访问数据。如果页表中对应的实际地址在磁盘上,就会发生缺页中断,这时需要操作系统分配一个实际的物理页给该进程使用,并将该物理页记录到页表当中。
这里写图片描述
这里还要讲下块表TLB的概念:快表是记录少数最近访问过的虚拟地址到实际内存地址的对应关系。因为每次进行虚拟地址到物理地址转换的时候都要去内存中查找页表,这将会耗费一定的开销。为了快速得到对应的内存地址,就同时在TLB里面查找。谁先找到就用谁的。这里也是基于时间局部性原理,认为最近被访问的虚拟内存在将来的一段时间内也有可能被访问。
高速缓存存储器cache:cache是内存的一部分最近访问过的数据及其附近的内容。它是基于空间局部性原理,认为内存中访问过的数据,它附近的数据也将会被访问过。
这里写图片描述
首次访问一个用户空间的地址
下面我们来讲一下,在请求页式机制下,程序访问一个地址所经历的流程。
(1)从页表或TLB中查找物理地址
这里写图片描述
首先,分页地址机构自动将逻辑地址分为页号与页内地址,并对地址进行越界检查,然后将页号送入快表中进行并行查找,如果从快表中找到所需页号,则将快表中对应的物理块号送物理地址寄存器,并修改访问位和修改位;否则将页表始址与页号和页表项长度的乘积相加,得页表中位置。
如果页面已在内存,修改快表;否则,调用缺页中断,从外存调入页面,并修改快表,再从快表中得到内存块号,送入物理地址寄存器,并修改访问位和修改位;最后将页内地址送入物理地址寄存器。
(2)缺页处理流程:在(1)中提到,如果页面不在内存中,即页面在磁盘上,则需要调用缺页中断。
这里写图片描述
缺页中断分为两种情况:
a.内存未满,这时直接让CPU从外存读取缺失的页面,通过启动I/O硬件将一页从外存换入内存中并修改页表,再进行一次正常的页表访问就可以形成物理地址了。
b.内存已满,这时不能直接从外存中调入缺失的页面,需要在主存中腾出一个空间出来给缺失的页面。关于如何腾出内存空间有页面替换算法,比如随机替换、最近最少使用(LRU)等算法。选择一个页面换出后,就启动I/O硬件读入缺失的页面,修改页表,重新进行物理地址解析。
(3)从cache或主存里面查找数据
得到了物理地址之后,就可以向内存发送读取数据的请求了。
根据主存地址空间映射到cache地址空间的方式,分为直接映射,全相联映射和组相联映射三种方式。
下面就以直接映射为例讲讲访存过程。
这里写图片描述
直接映射将一个物理地址分为三个部分:主存块标记、cache块号、块内地址;
然后根据块号,查看cache对应的主存标记会否跟物理地址的主存块标记一致,如果相同,还要看有效位是否为1,如果为1,则表示有效,数据直接从cache中获取,不需要经过主存。
如果出现标记位不相等或者有效位为0的情况,则不命中,这时候需要从主存中将数据取出送往CPU和cache对应块中,并将有效位置1,以便下次访问相同的物理地址时就能够在cache中直接获取地址了。

访问cache在几个时钟周期内,而访问内存需要数百个时钟周期,访问磁盘更甚,需要几百万个时钟周期,因此发生缺页中断的时间开销是很大的,cache缺失则次之。但是一般的程序都有局部性特征,采用合适大小的cache和替换算法,以及TLB,可以让命中率达到较好情况(90%以上),cache缺失和缺页仅发生在开始或者极少数的情况,使得访问存储器的数据的速度达到很快。

总结

这篇博客主要讲了程序在运行时的一些事情。其中重点是要理解存储系统的分层以及访问数据的过程。就这样,通过小明学C++这个例子,从计算机网络讲到操作系统,也算是将计算机基础知识都讲解了一遍,希望能够对计算机的运行机制有着更深的理解。

原创粉丝点击