1.1 信息就是位+上下文.
1 //实例代码: 2 #include<stdio.h> 3 int main(){ 4 printf("hello ,world!\n"); 5 } 6 7 /*大多数的现代系统都使用ASCII标准来表示文本字符,自重方式实际上就是 8 *用一个唯一的单字节大小的整数值来表示每个字符.表示如下: 9 */ 10 11 /* # i n c l u d e <sp> < s ......... 12 * 35 105 110 99 108 117 100 101 32 60 115 ......... 13 * 14 * ............ 15 *......... 16 */
源程序:源程序就是一个由0/1组成的位序列.8个位被组成一组,成为字节.每个字节表示程序中某个文本字符.
文本文件:在编辑器中存储的文本,最终都会被存为以ascii 码形式的文本,仅以ascii 字符组成的文件称为文本文件,其他文件都成为二进制文件.
文本文件表示方法(add.c,add.cpp等等.)都说明了一个思想方法:系统中所有的信息-------包括磁盘文件,存储器中的程序,存储器中存放的用户数据以及网络上传送的数据,都是由一串位表示.区分不同数据对象的唯一表示方法是我们读到的这些数据对象的上下文.比如在不同的上下文中,一个同样的字节序列可能表示一个整数,浮点数,字符串或者机器指令.
1.2 程序被其他程序翻译成不同的格式.
目标程序:每一条高级语言都必须被其他程序(编译器,解释器等)转化为一系列的低级机器语言指令.然后这些指令按照一种称为可执行目标程序的格式打好包,并以二进制磁盘文件的形式存放起来.目标程序也称为可执行目标文件.
unix系统上,c源程序到目标文件的转化由编译器驱动程序完成: unix> gcc -o programName programName.c
这个翻译过程可分为四个阶段完成,相对应四个阶段程序:预处理器,编译器,汇编器和连接器.这四个阶段程序构成了编译系统(compilation system).
1) 预处理阶段(Preprocessing phase): 预处理器将(cpp)处理以" # "开头的命令.通过执行改命令,修改源程序.如#include<stdio.h>命令告诉预处理器,将系统头文件stdio.h中的内容插入到程序文本中,得到以" .i " 为后缀扩展名结尾的另一个c程序.
2) 编译阶段(Compilation phase ):编译器(ccl)将文本文件" xxx.i " 翻译成文本文件" xxx.s".它是一个汇编语言程序.该汇编语言程序中的每条语句都以一种标准的文本格式确切的描述一条低级机器语言指令.
3) 汇编阶段(Assembly phase):汇编器(as)将xxx.s翻译成机器语言指令,把这些指令打包成一种可重定位目标程序的格式,并将其结果保存在目标文件xxx.o中.xxx.o文件是一个二进制文件,它的字节编码是机器语言指令,而不是字符.
4) 链接阶段(Linking phase):为了提高编译系统的效率.每个c编译器都会将一些标准的c库函数单独预编译好,放到对应的目标文件中.当我们在程序中调用这些标准函数时,就必须以某种方式将它们合并,并得到最终的一个可执行目标文件.连接器(ld)就负责处理这种合并.该目标文件可以被加载到内存中,又系统执行.
1.3 了解编译系统工作过程的益处.
1) 优化程序性能.
2) 理解链接时出现的错误.
3) 避免安全漏洞.(牛逼的东西...,以上都会在后续中将到.)
1.4 处理器读并解释存储在存储器中的指令
经过编译系统四个阶段处理的源程序,都被翻译成了可执行目标文件,并被存放在磁盘上.unix os 上运行该可执行文件,我们将它的文件名输入到称为外壳( shell )的应用程序中:
shell 是一个命令行解释器,它输出一个提示符,等待用户输入一个命令行,然后执行该命令行如果命令行第一个单词不是一个内置的shell 命令,那么shell就会假设这是一个可执行的文件名,并加载运行这个文件名. 如以上第一条命令行, "./ "为当前工作目录 ,hello 被默认当成可执行文件名被加载执行. 执行完该目标程序后,shell 随后会输出一个提示符,等待下一个输入的命令行.
1.4.1 系统硬件组成.
为了理解程序运行是在我们的计算机上发生了什么事情.我们先要对计算机硬件作简单的介绍.总的硬件组成如图所示:
1.总线
将系统各个部分连接起来的是一组电子管道,称作总线.它能携带信息字节并负责在各个部件间传递.总线由i/o总线,系统总线和存储总线三部分组成.
字(word):通常总线被设计为定长的字节块,也称为字(1字 = 2字节).
字长:字中的字节数(即字长),是系统的一个基本的参数.目前一般为4个字节机(32位)和8位字节机(64位).
2.i/o设备
输入/输出设别是系统与外部世界的联系通道.常见的i/o设备有:键盘,显示器,光驱中,鼠标,磁盘等.每个I/O设备都通过一个控制器或适配器与I/O总线相连.
控制器与适配器之间的区别:主要在与它们的封装方式.控制器通常是置于I/O设备本身或者系统的主印制电路板(主板)上的芯片组,而适配器则是一快插在主板插槽上的卡.功能都是在I/O总线和I/O设备之间传递消息.
3. 主存
所谓的主存是一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据.
从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成;逻辑上来说,存储器是一个闲闲的字节数组,每个字节都有唯一的地址(即数组索引),这些地址是从零开始的.一般来所,每条机器指令都由不同数量的字节构成.
4. 处理器
中央处理单元(cpu),简称处理器,是解释或执行存储在主存中指令的引擎.处理器的核心是一个字长的存储设备或寄存器,称为程序计数器(PC).程序计数器在任何时候都指向主存中的某条机器语言指令(即含有该条指令的地址.)
从系统通电开始,知道系统断电.处理器一直从程序计数器指向的存储器处读取指令,解释指令中的位,执行该指令指示的简单操作,然后更新PC,使其指向下一条指令,而此指令并不一定与上一条指令相邻.
上述所说的操作是围绕着主存,寄存器文件(register file)和算术/逻辑单元(ALU)进行的.寄存器文件是一个小的存储设备,由一些1字长的寄存器组成,每个寄存器都有唯一的名字.算术/逻辑单元(ALU)计算新的数据和地址值.
通常,cpu执行的操作有:
加载: 把一个字节 或者 一个字从主存复制到寄存器,以覆盖寄存器原来的内容.
存储: 把一个字节 或者 一个字从寄存器复制到主存的某个位置,以覆盖这个位置上原来的内容.
操作: 把两个寄存器的内容复制到ALU,ALU把这两个字做算术操作,并将结果存放到另一个寄存器中,以覆盖该寄存器中原来的内容.
跳转: 从指令本身中抽取一个字,并将这个字复制到程序计数器(PC)中,以覆盖PC中原来的值.
指令结构:描述的是每条机器代码指令的效果.
微体系结构:描述的是处理器实际上是如何实现的.
1.5. 高速缓存至关重要
根据机械原理,较大的存储设备要比较小的存储设备运行的慢,而快速设备的造价远高于同类的低速设备.类似的,一个典型的寄存器文件只存储几百字节的信息,而主存里可以放几十亿字节,然而处理器从寄存器文件中读数据的速度比从主存中读取几乎要快100倍.
高速缓存存储器:高速缓存存储器是系统设计者针对处理器与主存之间的差异,采用了更小,更快的存储设备.它作为一个暂时的集结区域,用来存放处理器可能会需要的信息.典型的系统高速缓存存储器如图所示:
L1:位于处理器芯片上的L1高速缓存容量可以达到数万字节,访问速度几乎和访问寄存器文件一样快.
L2:一个容量为数十万到数百万字节的更大的L2高速缓存通过一条特殊的总线连接到处理器.进程访问L2告诉缓存的时间要比访问L1告诉缓存的时间长5倍,但仍比访问主存的时间快5~10倍.
比较新的,处理能力更强大的系统甚至有三级高速缓存:L1,L2,L3.
静态随机访问存储器(SRAM):高速缓存是用一种叫做静态随机访问存储器的硬件技术实现的.
1.6. 存储设备形成层次结构
实际上,每个计算机系统中的存储设备都被组织成了一个存储器层次结构.如图示.在这个层次结构中,从上到下,设备变得访问速度越来越慢,容量越来越大,并且每字节的造价也越来越便宜.
存储器层次结构的主要思想是一层上的存储器作为低一层存储器的高速缓存。因此,寄存器文件就是 L1 的高速缓存,L1 是 L2 的高速缓存,L2 是 L3 的高速缓存,L3 是主存的高速缓存, 而主存又是磁盘的高速缓存。
1.7.操作系统管理硬件.
我们可以把操作系统看成是应 用程序和硬件之间插入的一层软件.如图示:
操作系统有两个基本功能 :1)防止硬件被失控的应用程序滥用。2)向应用程序提供简单一致的机制来控制复杂而又通常大相径庭的低级硬件设备。
操作系统通过几个基本的抽象概念(进程、虚拟存储器和文件)来实现这两个功能。如图示:
进程:进程是操作系统对一个正在运行的程序的一种抽象.在一个系统上可以同时运行多个进程, 而每个进程都好像在独占地使用硬件。而并发运行,则是说一个进程的指令和另一个进程的指令 是交错执行的。
无论是在单核 还是多核系统中,一个 CPU 看上去都像是在并发地执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。例如: 进程A可以为shell程序,进程B为hello程序.当进行shell输入后,键入回车健就会执行上下文切换.执行hello程序,输出后.切换回到shell程序.
线程:在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
虚拟存储器:虚拟存储器是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。每个进程看到的是一致的存储器,称为虚拟地址空间。 如图所示:
每个进程看到的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能。各部分简单介绍如下(从底部往上):
程序代码和数据区: 在进程一开始运行时就被规定 了大小,它是直接按照可执行目标文件的内容初始化的,在 示例中就是可执行文件 hello .
堆: 堆可以在运行时动态地扩展和收缩(例如:调用申请/释放内存的函数时).
共享库: 大约在地址空间的中间部分是一块用来存放像 C 标准库和数学库这样共享库的代码和数据的区域.(此处以c为例,若为java,则可能为java标准库函数与数据等).
栈: 位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用.它也可以进行动态收缩.
内核虚拟存储器: 内核总是驻留在内存中,是操作系统的一部分。地址空间顶部的区域是为内核保留的,不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。
虚拟存储器的运作需要硬件和操作系统软件之间精密复杂的交互,包括对处理器生成的每个地址的硬件翻译。其基本思想是把一个进程虚拟存储器的内容存储在磁盘上,然后用主存作为磁盘的高速缓存.
文件:文件就是字节序列.
1.8.系统之间利用网络通讯.
从一个单独的系统来看,网络可视为一个 I/O 设备,如 图 1-14 所示。当系统从主存将一串字节复制到网络适配器时,数据流经过网络到达另一台机器, 而不是其他地方,例如本地磁盘驱动器。相似地,系统可以读取从其他机器发送来的数据,并把 数据复制到自己的主存。
假设用本地主机上的 telnet 客户端连接远程主机上的 telnet 服务器。在我们登录到 远程主机并运行外壳后,远端的外壳就在等待接收输入命令。从这点开始,远程运行 hello 程 序包括(如图 1-15 所示)的五个基本步骤:
1.9.重要主题
并发:并发(concurrency)是一个通用的概念,指一个同时具有多个活动的系统 ;
并行: 而术语并行(parallelism)指的是用并发使一个系统运行得更快。并行可以在计算机系统的多个抽象层次上运用。在此,我们按照系统层次结构中由高到低的顺序重点强调三个层次:
1. 线程级并发:
使用线程,我们能够在一个进程中执行多个控制流.传统意义上,这种并发执行只是模拟出来的,是通 过使一台计算机在它正在执行的进程间快速切换的方式实现的,这种并发形式允许多个用户同时与系统交互.
单处理器系统:在以前,即使处理器必须在多个任务间切换,大 多数实际的计算也都是由一个处理器来完成的。这种配置称为单处理器系统。
多处理器系统:当构建一个由单操作系统内核控制的多处理器 组成的系统时,我们就得到了一个多处理器系统。多核处理器是将多个 CPU(称为“核”)集成到一个集成电路芯片上。
超线程:有时称为同时多线程(simultaneous multi-threading),是一项允许一个 CPU 执行多个控制流的技术。常规的处理器需要大约 20 000 个时钟周期 做不同线程间的转换,而超线程的处理器可以在单个周期的基础上决定要执行哪一个线程。这使 得 CPU 能够更好地利用它的处理资源。
多处理器的使用可以从两个方面提高系统性能:首先,它减少了在执行多个任务时模拟并发 的需要。 其次,它可以使应用程序运行得更快。
2. 指令级并行:
在较低的抽象层次上,现代处理器可以同时执行多条指令的属性称为指令级并行.如果处理器可以达到比一个周期一条指令更快的执行速率,就称之为超标量(superscalar) 处理器。
3. 单指令、多数据并行:
在最低层次上,许多现代处理器拥有特殊的硬件,允许一条指令产生多个可以并行执行的操 作,这种方式称为单指令、多数据,即 SIMD 并行。提供这些 SIMD 指令多是为了提高处理影像、声音和视频数据应用的执行速度。
计算机系统中抽象的重要性:
在处理器里,指令集结构 提供了对实际处理器硬件的抽象。使用这个抽象,机器代码程序表现得就好像它是运行在一个一 次只执行一条指令的处理器上。底层的硬件比抽象描述的要复杂精细得多,它并行地执行多条指 令,但又总是与那个简单有序的模型保持一致。只要执行模型一样,不同的处理器实现也能执行 同样的机器代码,而又提供不同的开销和性能。
四个抽象 :
文件是对 I/O 的抽象,
虚拟存储器是对程序存储器的抽象,
而进程是对一个正在运行的程序的抽象。
虚拟机,它提供 对整个计算机(包括操作系统、处理器和程序)的抽象。