CSAPP 第三、五、六、七章

Chapter 3 程序的机器级表示: GCC C语言编译器以汇编代码形式产生输出,汇编代码是机器代码的文本表示,给出程序中的每一条指令。本章近距离观察汇编代码。

         3.2.1机器级代码:对于machine-level编程来说,有两种抽象十分重要:1.指令集体系结构(Instruction Set Architecture ,ISA)定义machine-level程序的格式以及行为,它定义了处理器状态,指令格式以及每条指令对状态的影响。2.machine-level程序使用的内存地址是虚拟地址,仅仅将存储器看做是按字节寻址的数组。

         3.3数据格式:Intel使用术语word表示16位数据类型,C语言中标准int值存储为双字。第三版和第二版分别给出IA32和x86-64中C语言数据类型的大小。

         3.4访问信息:一个IA32CPU包含一组8个寄存器来存放32-bit的值,对应的x86-64CPU含一组16个存储64位值的通用寄存器。指令可以对16个寄存器的低位字节中存放的不同大小的数据进行操作。根据惯例,栈倒过来画,栈顶在底部。X86-64中,栈向低地址方向增长。

         3.5算术和逻辑操作 3.6控制:条件码及访问,跳转指令,条件控制,循环,switch语句

 

Chapter 5 优化程序性能:编写高效程序要做到以下几点:1.必须选择一组适当的算法和数据结构。2.必须编写出编译器能够有效优化的可以转化为高效代码的源代码。3.对运算量打的计算,任何分成多个部分。程序优化第一步是消除不必要的工作,让代码尽可能有效地执行任务。第二步是利用处理器提供的指令级并行能力,同时执行多条指令。

         5.1优化编译器的能力和局限性:大多数编译器向用户提供了一些对它们使用的优化的控制,如指定优化的级别。同时,编译器要保证只对程序使用安全的优化:程序优化前后行为一致

         5.2表示程序性能 5.3 程序示例 5.4 消除循环低效性 5.5 减少过程调用 5.6 减少过程调用 5.7 理解现代处理器前面部分可以在编译原理内容中学习到。后面几个小结也是太概括性,总结性地介绍了

 

Chapter 6 存储器层次结构:实际上,没存系统是一个具有不同容量,开销和访问时间的存储设备层次结构。CPU寄存器保存最常用的数据,靠近CPU的小的,快速的缓存(告诉缓存存储器),作为一部分存储在主存中的数据和指令的缓冲区域。局部性属性:具有良好局部性的程序趋向于一次次访问相同数据项集合或者临近数据项集合且趋向于访问层次结构中较高层次的数据项。    

         6.1存储技术:

6.1.1 随机访问存储器(RAM)分为两类:静态和动态的SRAM,DRAM。SRAM更快,但也更贵。

SRAM可以用作高速缓存存储器,也可以在CPU芯片上,片下。而DRAM用作主存以及图形系统的帧缓冲区。

SRAM将每个位存储在一个双稳态(bistable)存储器单元中,每个单元用一个六晶体管电路来数显,电路可以无限期保持两个不同的电压配置或者状态之一。DRAM将每个位存储为对一个电容的充电,其存储单元对干扰非常敏感,电容电压被扰乱后,它永远不会恢复,用在数码相机和摄像机中的传感器中。

传统的DRAM:DRAM芯片中的单元被分成d个supercell,每个supercell都由w个DRAM单元组成,每个DRAM芯片被连接到内存控制器的电路中。DRAM芯片又封装在内存模块中。增强的DRAM介绍以及ROM的介绍。主存通过总线访问。

6.1.2磁盘存储:磁盘由盘片组成,每个盘片有两面,片中央有一个可旋转的主轴,使得盘片可以按一定速率旋转。每个表面由一组磁道(一个圆),每个磁道划分为一组扇区,柱面是由每个面的相同磁道组成的。几个重要的时间以及具体结构。

         6.2局部性:一个编写良好的程序通常具有良好的局部性,即倾向于引用邻近于其他最近引用过的数据项。两种形式:时间和空间。。。

         6.3存储器层次结构:这是一种组织存储器系统的方法。其中高速缓存(cache)是一个重要的通用概念:第k层更快更小的存储设备作为k+1层更大更慢的存储设备的缓存。Cache hit, cache miss的概念,管理。

         6.4高速缓存存储器:通用cache组织结构:对于一个有2的m次方个不同地址的机器,器cache被组织成一个有S = 2的s次方个cache set的数组。而每个存储器地址有m位,其中t位标记位,s位位索引,b位块偏移。每个组包含E个cache line。每个行由一个B = 2的b次方字节的block组成,一个valid bit指明这个行是否包含有意义的信息,还有t = m – (b + s)个tag bit,它们唯一地标识存储在这个cache line中的block。一般cache的结构可以使用元组(S, E, B, m)来描述。(组,行,字节)其中,cache大小C = S * E *B

根据每个组的cache line数量分类的几种映射方式介绍:1.直接映射,每个组只有一行的cache。CPU向cache请求一个字的过程:1)组选择2)行匹配3)字抽取 2.组相联映射,每个组有多个行的cache,映射到这个组的block可以放在该组的任何一行。对比直接映射,需要检查多个行的有效位以及标识位 3.全相联映射,一个包含所有行的组组成。地址变成了只有标记和位偏移。(对比清华版的计组这里给出了cache组织结构,更加清晰)

写的问题:数据一致性保持,写数据策略的选择问题。i7处理器cache层次结构的例子

         6.5编写高速缓存友好的代码:基本方法:1)让最常见的情况运行得快,注意力集中在核心函数里的循环上2)尽量减少每个循环内部的缓存不命中的数量。

         6.6cache对程序性能的影响:程序从存储系统用中读数据的速率称为读吞吐量(read throughput).每个计算机有表明其存储器系统能力特色唯一的memory mountain.(封面的图片就是i7的图片)重新排列循环以提高空间局部性。在程序中利用局部性。

 

Chapter7 Linking 链接是将各种代码和数据段收集并组合成一个单一文件的过程,这个文件可以被加载进内存并执行。链接可以执行在编译器,加载时,甚至运行时。现代系统中,链接由链接器执行。允许单独编译时,可以将一个大型应用程序分解为小的可管理的模块(可以单独编译和修改)。

         7.1编译器驱动程序:大多数编译系统提供compiler driver,它代表用户在需要时调用语言预处理器,编译器,汇编器以及链接器。

         7.2静态链接:以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的,可以加载和运行的可执行目标文件作为输出。链接器必须完成的两个主要任务:符号解析和重定位

         7.3目标文件:三种形式:1.可重定位目标文件2.可执行目标文件3.共享目标文件编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件。一个目标模块是一个字节序列,而一个目标文件是一个以文件形式存放在磁盘中的目标模块。

         7.4可重定位目标文件:一个ELF可重定位目标文件的格式,header以一个16字节的序列开始,描述了生成该文件的系统的字的大小和字节顺序,header剩下的部分包含帮助链接器的语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型,机器类型,section header table的文件偏移以及该表条目的大小和数量。

         7.5符号和符号表:每个可重定位目标模块m都有一个符号表,它包含m定义和引用的符号的信息,在链接器的上下文中,有三种不同的符号:1.模块m定义并可以被其他模块引用的全局符号2.其他模块定义并被模块m引用的全局符号3.只被模块m定义和引用的局部符号。符号表又汇编器构造,使用编译器输出到汇编语言文件中的符号。

         7.6符号解析:链接器解析符号引用的方法是将每个引用和它输出的可重定位目标文件的符号表中一个确定的符号定义关联起来。编译器只允许每个模块中每个局部符号有一个定义,静态局部变量也会有本地链接器符号,编译器要确保它们有唯一的名字。

当编译器遇到一个不在当前模块中定义的符号时,会假设该符号是在其他模块中定义的,生成一个链接器符号表条目,并把它交给链接器处理,若链接器在它任何输入模块中都找不到该符号的定义,就输出一条错误信息并终止。

7.6.1链接器解析多重定义的全局符号:链接器的输入时一组可重定位目标模块。Linux链接器的方法:编译时,编译器向汇编器输出每个全局符号(强或者弱的),汇编器将这个信息编码在可重定位目标文件的符号表中。其中函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。处理多重定义的符号名的规则:1.不允许有多个同名的强符号2.若一个强符号和多个弱符号同名,则选择强符号3.有多个弱符号,则从中任意选择一个。其中2,3条规则将产生潜在的bugs。

7.6.2与静态库链接:所有编译系统都提供一种机制,将所有相关的目标模块打包称为一个单独的问题件,称为静态库,它可以用做链接器的输入。当链接器构造一个可执行文件时,它只复制静态库里被应用程序引用的目标模块(比如库函数)。这种方法的优点是将编译器的实现与标准函数的实现分离出来,缺点是系统中每个可执行文件都包含一份标准函数集合的完全副本(不是只复制被引用的吗),对标准函数的修改需要重新编译整个源文件。相关函数可以被编译为独立的目标模块,之后封装成一个单独的静态库文件,应用程序可以在命令行中指定文件名来使用这些函数。

7.6.3链接器使用静态库来解析引用;

         7.7重定位:链接器完成符号解析时,就把代码中每个符号引用和一个符号定义相关联。链接器知道其输入目标模块中代码section和数据sections的确切大小,就开始重定位,这个步骤将合并输入模块,为每个符号分配运行地址。分为两步:1.重定位sections和符号定义:链接器将所有相同类型的sections合并为同一类型的新的section,之后将运行时内存地址赋给新的这个聚合section,赋给输入模块定义的每一个section,以及赋给输入模块定义的每个符号。这一步完成后,程序中每条指令和全局变量都有唯一地运行时内存地址。2.重定位section中的符号引用:链接器修改代码section和数据section中对每个符号的引用,使它们指向正确的运行时地址,这个步骤依赖于可重定位目标模块中称为重定位条目(relocation entry)的数据结构。

7.7.1 relocation entry:汇编器遇到最终位置未知的目标引用,会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。7.7.2重定位算法

         7.8可执行目标文件:figure7.13概括了典型ELF可执行文件中各类信息。

         7.9加载可执行目标文件

         7.10动态链接共享库:共享库用来解决静态库缺陷。它是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。该过程称为动态链接,由一个动态链接器程序执行。共享库也称为共享目标,微软操作系统中称为DLL。

         7.11从应用程序中加载和链接共享库:应用程序还可能在它运行时要求动态链接器加载和链接某个共享库,而无需在编译器时将那些苦链接到应用中。动态链接应用例子:分发软件;构建高性能web服务器。

         7.12位置无关代码:共享库一个主要目的就是允许多个正在运行的进程共享内存中相同的库代码,因而节约宝贵的内存资源。系统可以把共享模块的代码section加载到内存的任何位置而无需链接器修改。这种方法可以使无限多个进程共享一个代码section的一个副本,可以加载而无需重定位的代码称为位置无关代码(position-independent code PIC)。

         7.13库打桩机制(第三版)Linux链接器支持的技术,它允许你截获对共享库函数的调用,取而代之执行自己的代码。使用它可以追踪对某个特殊库函数的调用次数,验证以及追踪它的输入输出值,以及替换实现。给定一个需要打桩的目标函数,创建一个包装函数,其原型和目标函数完全一样,使用特殊的打桩机制,可以欺骗系统调用包装函数。打桩可以发生在编译时,链接时或运行时。   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值