上一章LZ给各位展示了一下hello程序的简单执行过程,可以看出在这一过程当中,系统在数据的传输上花费了大量的时间。硬件开发商为了减少这种数据传输的时间成本,采用一种高速缓存的技术去减少这种时间成本。
高速缓存被置放于处理器当中,与处理器中的寄存器文件直接进行数据交换,这样大大减少了数据传输的时间成本,使得程序的运行速度可以得到数倍的提升。而作为一个程序猿,如果可以适当的利用高速缓存去存放一些程序运行过程中可能会经常使用的数据,可以将程序的运行速度提高数倍甚至数个数量级。
由此就可见高速缓存的重要性,下图展示了硬件分布中,高速缓存的位置。
存储设备的金字塔
当今的计算机系统当中,基本上全部都置入了各种各样的存储设备,这些存储设备呈明显的层次结构,它们的特点是容量越大,速度越慢。因此如果按照容量和速度将它们以图示的方式呈现的话,则看起来就像是一个金字塔,如下所示。(非常感谢群里的猿友【夕惕若厉无咎】提供的英文电子版,这下LZ可以省去很多画图的功夫了)
不过这个图是英文版的,LZ稍微解释下,有些特别明显的LZ就不解释了,如果有哪位猿友实在不明白,可以到LZ的群里提问。
左边的意思是更小更快更贵的存储设备,包括寄存器以及L1-L3的高速缓存,以及更大更慢更便宜的存储设备,包括主存、本地磁盘以及远程存储设备。通常意义下,我们将上一层的存储设备作为当前存储设备的高速缓存,比如L1的高速缓存是寄存器,L2的高速缓存是L1,以此类推。
操作系统是硬件的manager
操作系统是帮我们操控硬件的软件,它就像是应用程序与硬件的中间者,在两者之间扮演一个协调、管理的角色。它们的关系如下图。
操作系统提供了几个我们熟悉的概念去表示硬件设备,比如文件、虚拟存储器、文件。它们表示的硬件设备如下图所示。
可以看出文件是对I/O设备的抽象描述,而虚拟存储器是对主存和I/O设备的统称,最后,一个进程在此基础上又加入了处理器。
进程
进程是操作系统对一个正在运行的程序的抽象。操作系统会记录每一个进程的状态,这些状态就称作进程的上下文。这些状态主要包括了PC,寄存器以及主存的当前内容。当操作系统在进程间切换的时候,也会切换相应的上下文,从而保证进程恢复到之前的状态。
在进程当中,又被计算机界的大神们引入了线程的概念,这些线程可以共享进程级的代码与数据,这种共享一般比进程间的共享更加高效。
虚拟存储器
虚拟存储器是一种抽象描述,从物理上讲,它包含了I/O设备以及主存。在逻辑上讲,虚拟存储器被描述为虚拟地址空间。下图为进程的虚拟地址空间表示。
这里的地址自下向上依次增大,可以看出,图中标注了起始地址,分别为0x08048000(32位)以及0x00400000(64位),然后向上分别是只读代码和数据、读写数据、运行时堆、共享库的内存映射区间、用户栈以及内核虚拟内存区域。
看完这个图,LZ有一个疑问,就是这两个32位和64位的起始地址是从何而来,于是小小的简单探索了一下。
LZ在32位的linux系统上做了个测试,我们随便写一个C程序,然后使用GCC加上参数-Wl,--verbose去编译这个文件,于是我们便可以在链接器脚本里看到这些内容。如下图所示。
在里面我们可以看到0x08048000这个内存地址,__executable_start表面看来的意思是可执行的起始位置。具体这个数字的来由LZ没有找到,我们可以先存着这个疑问,或许在书中后面的内容会解答这个疑问。(小提示:有些东西一时搞不明白,不要沉迷于此,会得不偿失)
从这个地址向上则分了大致五个存储区域。
程序代码和数据:这些内容的起始地址就是0x08048000,首先是代码,然后是一些全局变量。
堆:是运行时可以动态扩展的一部分内存区域,它可以由malloc和free这样的标准库函数操作。
共享库:用于存放共享库的代码和数据。
栈:在用户虚拟地址空间的顶部是栈,这部分区域与函数的执行有密切的关系。
内核虚拟存储区域:内核是操作系统的一部分,就LZ粗浅的理解,内核也可以看做是一个进程,它在计算机运行期间总是在运行着,因此这部分内存区域对用户程序是不可见的,通俗的说就是不能用。
文件
文件是I/O设备逻辑上的概念,它其实就是字节序列,也就是1和0组成的一些信息。因此所有的I/O设备,包括磁盘、键盘、鼠标、显示器都可以看成是文件。
网络
前面说了,所有的I/O设备其实都是文件这一抽象概念的具体表现,那么网络其实也是文件的一种,因为说到底,它也可以被看做是一系列的字节序列。网络适配器的作用就是给计算机输入一堆被传送过来的字节序列,这里面可能包括图片、文字,甚至可能是代码等等。
文章小结
本文主要介绍存储设备的层次以及操作系统中的三个抽象概念,下一章是并发与并行的简单介绍,也是计算机简介的最后一章。