目的:从最基本的编译,静态链接到操作系统如何转载程序,动态链接及运行库和标准库的实现,和一些操作系统的机制。了解计算机上程序运行的一个基本脉络。
1.2变不离其宗计算机最关键的三个部分:CPU,内存,I/O控制芯片。
北桥 SMP和多核现在CPU已经达到物理极限,被4GHz所限制,于是,开始通过增加CPU数量来提高计算机速度。
对称多处理器(SMP):最常见的一种形式。每个CPU在系统中所处的地位和所发挥的功能是一样,是相互对称的。但在处理程序时,我们并不能把他们分成若干个不相干的子问题,所以,使得多处理器速度实际提高得并没有理论上那么高。当对于相互独立的问题,多处理器就能最大效能的发挥威力了(比如:大型数据库,网络服务等)。
对处理器由于造价比较高昂,主要用在商用电脑上,对于个人电脑,主要是多核处理器。
多核处理器:其实际上是(SMP)的简化版,思想是将多个处理器合并在一起打包出售,它们之间共享比较昂贵的缓存部件,只保留了多个核心。在逻辑上看,它们和SMP完全相同。
系统软件:一般用于管理计算机本地的软件。
主要分为两块:
计算机系统软件体系结构采用一种层的结构。
每个层次之间都需要相互通信,那么它们之间就有通信协议,我们将它称为接口,接口下层是提供者,定义接口。上层是使用者,使用接口实现所需功能。
除了硬件和应用程序,其他的都是中间层,每个中间层都是对它下面的那层的包装和扩展。它们使得应用程序和硬件之间保持相对独立。
从整个层次结构来看,开发工具与应用程序属于同一个层次,因为它们都使用同一个接口—操作系统应用程序编程接口。应用程序接口提供者是运行库,什么样的运行库提供什么样的接口。winsows的运行库提供Windows API,Linux下的Gliba库提供POSIX的API。
运行库使用操作系统提供的系统调用接口。
系统调用接口在实现中往往以软件中断的方式提供。
操作系统内核层对于硬件层来说是硬件接口的使用者,而硬件是接口的定义者。这种接口叫做硬件规格。
操作系统的一个功能是提供抽象的接口,另外一个主要功能是管理硬件资源。
一个计算机中的资源主要分CPU,存储器(包括内存和磁盘)和I/O设备。下面从这3个方面来看如何挖机它们。
多道程序:编译一个监控程序,当程序不需要使用CPU时,将其他在等待CPU的程序启动。但它的弊端是不分轻重缓急,有时候一个交互操作可能要等待数十分钟。
改进后
分时系统:每个CPU运行一段时间后,就主动让出给其他CPU使用。完整的操作系统雏形在此时开始出现。但当一个程序死机的时候,无法主动让出CPU,那么,整个系统都无法响应。
目前操作系统采用的方式
多任务系统:操作系统接管了所有的硬件资源,并且本身运行在一个受硬件保护的级别。所有的应用都以进程的方式运行在比操作系统更低的级别,每个进程都有自己独立的地址空间,使得进程之间的地址空间相互隔离。CPU由操作系统进行同一分配,每个进程根据进程优先级的高低都有机会获得CPU,但如果运行超过一定的时间,CPU会将资源分配给其他进程,这种CPU分配方式是抢占式。如果操作系统分配每个进程的时间很短,就会造成很多进程都在同时运行的假象,即所谓的宏观并行,微观串行。
操作系统作为硬件层的上层,它是对硬件的管理和抽象。
对于操作系统上面的运行库和应用程序来说,它们只希望看到一个统一的硬件访问模式。
当成熟的操作系统出现后,硬件逐渐成了抽象的概念。在NUIX中,硬件设备的访问形式和访问普通的文件形式一样。在Windows系统中,图形硬件被抽象成GDI,声音和多媒体设备被抽象成DirectX对象,磁盘被抽象成普通文件系统。
这些繁琐的硬件细节全都交给了操作系统中的硬件驱动。
文件系统管理这磁盘的存储方式。
磁盘的结构:一个硬盘往往有多个盘片,每个盘片分两面,每面按照同心圆划分为若干磁道,每个磁道划分为若干扇区,每个扇区一般512字节。
LBA:整个硬盘中所有扇区从0开始编号,一直到最后一个扇区,这个扇区编号叫做逻辑扇区号。
文件系统保存了这些文件的存储结构,负责维护这些数据结构并且保证磁盘中的扇区能有效的组织和利用。
在早期计算机中,程序是直接运行在物理内存上的,程序所访问的都是物理地址。
那么如何将计算机有限的地址分配给多个程序使用。
直接按物理内存分配将产生很多问题:
一种解决办法:
中间层:使用一种间接的地址访问方法,我们把程序给出的地址看作一种虚拟地址。虚拟地址是物理地址的映射,只要处理好这个过程,就可以起到隔离的作用。
普通的程序它只需要一个简单的执行环境,一个单一的地址空间,有自己的CPU。
地址空间比较抽象,如果把它想象成一个数组,每一个数组是一字节,数组大小就是地址空间的长度,那么32位的地址空间大小就是2^32=4294967296字节,即4G,地址空间有效位是0x00000000~0xFFFFFFFF。
地址空间分为两种:
物理空间:就是物理内存。32位的机器,地址线就有32条,物理空间4G,但如果值装有512M的内存,那么实际有效的空间地址就是0x00000000~0x1FFFFFFF,其他部分都是无效的。
虚拟空间:每个进程都有自己独立的虚拟空间,而且每个进程只能访问自己的空间地址,这样就有效的做到了进程隔离。
基本思路:把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。虚拟空间的每个字节对应物理空间的每个字节。这个映射过程由软件来完成。
分段的方式可以解决之前的第一个(地址空间不隔离)和第三个问题(程序运行地址不确定)
第二问题内存使用效率问题依旧没有解决。
基本方法:把地址空间人为的分成固定大小的页,每一页大小有硬件决定或硬件支持多种大小的页,由操作系统决定页的大小。
目前几乎所有的PC上的操作系统都是4KB大小的页。
我们把进程的虚拟地址空间按页分割,把常用的数据和代码页转载到内存中,把不常用的代码和数据保存到磁盘里,当需要的时候从磁盘取出来。
虚拟空间的页叫做虚拟页(VP),物理内存中页叫做物理页,把磁盘中的页叫做磁盘页。虚拟空间的有的页被映射到同一个物理页,这样就可以实现内存共享。
当进程需要一个页时,这个页是磁盘页时,硬件会捕获到这个消息,就是所谓的页错误,然后操作系统接管进程,负责从磁盘中读取内容装入内存中,然后再将内存和这个页建立映射关系。
保护也是页映射的目的之一,每个页都可以设置权限属性,只有操作系统可以修改这些属性,这样操作系统就可以保护自己保护进程。
虚拟存储的实现需要依靠硬件支持,所有硬件都采用一个叫做MMU的部件来进行页映射。
CPU发出虚拟地址经过MMU转换成物理地址,MMU一般都集成在CPU内部。
多线程现在作为实现软件并发执行的一个重要方法,具有越来越重的地位。
什么是线程线程有时被称为轻量级的进程,是程序执行流的最小单位。
构成:
线程与进程的关系:
多线程可以互不干扰的并发执行,并共享进程的全局变量和堆的数据。
使用多线程的原因有如下几点:
线程的访问非常自由,它可以访问进程内存里所有数据,包括其他线程的堆栈(如果知道地址的话,情况很少见)。
线程自己的私用存储空间:
线程私用线程间共享(进程所有)
局部变量 全局变量
函数参数 堆上数据
TLS数据 函数里的静态变量
程序代码
打开的文件,A线程打开的文件可以由B线程读取
线程调度与优先级