本系列文章是本人读《深入理解计算机系统》时的摘抄和读书笔记,内容比较精简,可能会有错误,如想展开深入理解请阅读源书
序言
《深入理解计算机系统》适用于想深入理解计算机底层系统以及如何对应用程序影响的程序员。
你将学会一些实践技巧(比如如何避免奇怪数字错误);如何利用现代处理器和存储器的设计;如何优化c代码;编译器如何实现过程调用;如何避免缓冲区溢出错误带来的安全漏洞;如何识别和避免链接时出现的错误;如何编写自己的Unix shell、动态存储分配包、Web服务器;并发的好处和陷阱。
文章目录
序言1.1 信息就是位+上下文1.1.1 c语言的起源 1.2 程序被其他程序翻译成不同格式1.3 了结编译系统如何工作的好处1.4 处理器读并解释储存在内存中的指令1.4.1 系统的硬件组成1.4.2 运行hello程序 1.5 高速缓存至关重要1.6 存储设备形成层次结构1.7 操作系统管理硬件1.7.1 进程1.7.2 线程1.7.3 虚拟内存1.7.4 文件 1.8 系统之间利用网络通信1.9 重要主题1.9.1 Amdahl定律1.9.2 并发和并行1.9.3 计算机系统中抽象的重要性1.1 信息就是位+上下文
本章将从hello程序的生命周期来开始对系统进行学习,包括程序创建、在系统运行、输出信息、程序结束
hello.c
#include <studio.h>int main(){printf("hello world\n");return 0;}
程序的生命周期从源程序(源文件)开始,其实质是由值0 1组成的位(比特)序列,8位为一组,称为字节。每个字节表示程序中的某些文本字符。大部分计算机系统使用ASCII标准表示文本字符(用一个唯一的单字节大小的整数值表示每个字符)。像hello.c这样只由ASCII字符构成文件称为文本文件,其他文件称为二进制文件。
系统中所有信息都是由一串比特表示的,区分不同数据对象的唯一方式是读数据对象的上下文(比如在不同上下文中,同一字节序列可以表示为一个整数、浮点数、字符串或机器指令)。
1.1.1 c语言的起源
c语言是贝尔实验室的Dennis Ritchie创建的。
c语言一开始作为一种用于Unix系统的程序语言开发出来,大部分Unix内核,所有支撑工具和函数库都是用c语言编写的。
由于c语言的设计是由一人完成,所以它的特点是小而简单,也是这个特性使它易于学习和易于移植。
c语言的缺点和难点是指针的使用、缺乏有用的抽象显示支持(例如类、对象、异常)。
1.2 程序被其他程序翻译成不同格式
为了在系统上运行hello.c程序,c语句必须被其他编译器转化为一系列低级机器语言指令,指令被打包成可执行目标程序的格式,以二进制文件的形式存储起来。该步骤由编译器驱动程序完成。具体过程如下:
预处理阶段。预处理器(cpp)根据#开头的命令,修改原始的hello.c程序(比如#include<studio.h>告诉预处理器读取系统头文件studio.h的内容,并把它插入程序文本当中,获得另一个c程序hello.i文本文件。编译阶段。编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,一个汇编语言文本文件。(汇编语言为不同高级语言的不同编译器提供了通用的输出语言)。汇编阶段。汇编器(as)将hello.s翻译成机器语言指令,把指令打包成可重定位目标程序的格式,将结果保存在hello.o二进制文件中,使用文本编辑器打开它会看到一堆乱码。链接阶段。hello程序调用的printf函数,是每个c编译器都提供的标准c库中的一个函数,printf函数存在于一个名为printf.o的单独预编译好的目标文件中。连接器(ld)负责将printf.o和hello.o的合并,最后得到一个可执行目标文件(二进制文件),被加载到内存中,由系统执行。
1.3 了结编译系统如何工作的好处
优化程序性能。比如switch语句是否比ifelse语句更高效?函数调用的开销有多大?理解链接时出现的错误。比如理解错误报告中无法解析一个引用的意思?静态变量和全局变量的区别?避免安全漏洞。缓冲区溢出漏洞是大多数网络服务器安全漏洞的原因(因为很少程序员理解需要限制从不受信任的源接受数据的数量和格式)。1.4 处理器读并解释储存在内存中的指令
此时hello.c源程序被翻译成可执行目标文件hello.o,并被存储到磁盘中,要想在Unix系统中执行该文件,需要到shell(命令行解释器,输入命令行,执行命令)执行。
1.4.1 系统的硬件组成
总线:贯穿整个系统的一组电子管道。携带信息字节并负责各个部件中传递。通常被设计成传送定长的字节快,也就是字(word)。字的字节数是一个基本的系统参数,现在系统通常是8个字节(64位)。I/O设备:系统与外部联系的通道。I/O设备包括键盘、鼠标、显示器、磁盘。每一个I/O设备通过一个控制器(I/O设备本身或系统主板上的芯片组)或设配器(主板插槽上的卡)与I/O总线相连,用来在总线和设备之间传递信息,控制器与适配器之间的区别是它们的封装方式。主存:临时存储设备,cpu处理程序时存放程序和程序处理的数据。物理上说,贮存由一组动态随机存取存储器(DRAM)芯片组成。逻辑上说,存储器是一个线性字节数组每个字节有其唯一地址(数组索引),地址从零开始。处理器:中央处理单元,cpu,简称处理器,是解释执行存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC),任何时刻PC都指向主存中的某条机器语言指令。从系统通电到断电,处理器一直在不断执行PC指向的指令。处理器看上去是按照一个非常简单的指令执行模型来操作的,这个模型是由指令集架构决定的。在这个模型中,指令按照严格的顺序执行,对应的指令并不一定相邻。
除了PC,还有寄存器文件和算术逻辑单元(ALU)组成cpu。寄存器文件是一个小的存储设备,由一些单个字长的寄存器组成。ALU计算新的数据和地址值。
1.4.2 运行hello程序
shell程序执行运行hello程序的指令后,shell程序将字符逐一读入寄存器,再把它存入内存中。从磁盘中加载hello可执行文件中的代码和数据到主存。执行hello程序中的main程序中的机器语言指令,这些指令将hello world\n字符串从主存复制到cpu中的寄存器文件,再从寄存器文件复制到显示屏幕中。1.5 高速缓存至关重要
由于寄存器与主存间速度的不匹配,系统设计师使用更小更快的存储设备,高速缓存存储器(cache),作为暂时的存储器存放处理器可能需要的信息,位于cpu上的L1 cache 达到数万字节存储,L2 cache通关特殊总线链接到cpu中,L2比L1慢5倍左右,L1L2使用了一种叫静态随机访问存储器(SRAM)的技术其访问速度几乎和访问寄存器文件一样快。cache使用了局部性原理(数据和代码有访问局部区域的趋势)。
1.6 存储设备形成层次结构
按照速度和价格排序的存储器层次结构
L0:寄存器:保存来自L1 cache的字
L1:L1 cache(SRAM):保存来自L2 cache的行
L2:L2 cache(SRAM):保存来自L3 cache的行
L3:L3 cache(SRAM):保存来自主存的行
L4:主存(DRAM):保存来自磁盘的块
L5:本地磁盘:保存来自远程服务器的磁盘文件
L6:分布式文件系统、Web服务器
1.7 操作系统管理硬件
操作系统是硬件和应用程序之间的一层软件。
功能:(1)防止硬件被时空的应用程序滥用。(2)向应用程序提供简单一致的机制控制复杂的硬件设备。操作系统通过进程。(cpu、主存和IO设备的抽象)、虚拟内存(主存和IO设备的抽象)和文件(IO设备的抽象)来实现这两个功能。
1.7.1 进程
进程是操作系统对一个正在运行的程序的一个抽象。一个系统上可以同时运行多个进程,而每个进程都好像在独占硬件。并发运行,是一个进程的指令和另一个进程的指令交错执行。上下文切换,是指通过cpu在进程间切换以达到并发执行多个进程的效果。上下文,是操作系统保持跟踪进程运行所要的所有状态信息,比如PC和寄存器文件的当前值,主存内容。当进行上下文切换时,会保存当前进程的上下文,恢复新进程的上下文,新进程从上次停止的地方重新开始。
从一个进程到另一个进程的转换是由操作系统内核管理的,内核是操作系统代码常驻内存的部分。内核不是独立进程,是系统管理全部进程所用代码和数据结构的集合。
1.7.2 线程
一个进程由多个线程(执行单元)组成,每个线程运行在进程的上下文中,共享同样的代码和全局数据。
1.7.3 虚拟内存
虚拟内存是抽象概念,它为进程提供一个假象,即每个进程在独占主存。每个进程看到的内存都是一致的,称为虚拟内存空间。Linux中,地址空间最上面的区域是保留给操作系统中的代码和数据的,地址空间底部存放用户进程的代码和数据。
每个进程看到的虚拟地址空间由大量准确的区构成,每个区有专门的功能。
从低地址开始:
程序代码和数据。代码是从同一固定地址开始的,然后是c全局变量相对应的数据位置。该区域从进程运行开始就确定了大小堆。当调用malloc 和free等标准c库函数时,堆可以在运行时动态扩展和收缩。共享库。大约在地址空间的中间部分一块用来存放c标准库和数学库的共享库的代码和数据的区域。栈。用户虚拟地址空间顶部,用以实现函数调用,可以动态扩张和收缩。内核虚拟内存。应用程序不允许读写该区域,也不可直接调用内核代码定义的函数。
1.7.4 文件
文件是字节序列,所有IO设备甚至网络都可以看作是文件,文件向应用程序提供一个统一的视图,来看待所有IO设备。
1.8 系统之间利用网络通信
网络可以看作是一个IO设备,系统可以控制字节从主存直接复制到网络适配器中,而不用到达本地磁盘中,反过来亦是。
1.9 重要主题
系统是硬件和系统软件交织的结合体。
1.9.1 Amdahl定律
当我们对系统某部分加速时,其对系统整体性能影响取决于该部分的重要性和加速程度。
1.9.2 并发和并行
并发:一个同时具有多个活动的系统。
并行:用并发来使一个系统运行的更快。
线程级并发。多核处理器是将多个cpu集成到一个集成电路芯片中,每个核心有自己的L1和L2 cache,L1分两部分,一个存储最近取到的指令,另一个存储数据,L3 cache所有核共享。超线程,也称为同时多线程,允许一个cpu执行多个控制流的技术,超线程cpu可以在单个周期基础上决定要执行哪一个线程,使得cpu更高效利用资源。多处理器从两方面提高系统性能,一方面减少执行多个任务时模拟并发的需求;其次它可以使得应用程序(多线程程序)执行更快。指令级并行。指的是cpu可以同时执行多条指令。超标量指的是cpu可以达到比一个周期一条指令更快的执行速率。单指令、多数据并行(SIMD并行)。指的是允许一条指令产生多个可以并行执行的操作。
1.9.3 计算机系统中抽象的重要性
指令集架构:提供对实际cpu硬件的抽象。虚拟机:对操作系统+处理器+主存+IO设备的抽象。