深入理解计算机系统读书笔记三

高速缓存的写操作

假设缓存中已经缓存了字w(写命中)。在高速缓存更新了它的w的副本后,它有两种方式更新w在层次结构中紧接着低一层中的副本。

  1. 直写。就是立即将w的高速缓存块写回紧接着的低一层中。这种做法每次都会引起总线流量。
  2. 写回。写回会尽可能的推迟更新,只有当替换算法要驱逐这个更新过的块时,才会把他写道紧接着的第一层中。写回可以显著的减少总线流量,但是增加了复杂性。

假设缓存中没有缓存字w(写不命中),也有两种处理方式。
1.写分配(write-allocate),加载相应的第一层中的块到高速缓存中,然后更新高速缓存块,试图利用写的空间局部性,但缺点是每次写不命中都会导致一个块从第一层传送到高速缓存。
2.非写分配(not-write-allocate)。避开高速缓存,直接把这个字写到低一层中。

实际的高速缓存层次结构

在实际中,高速缓存既保存数据,也保存指令。只保存指令的高速缓存被称为i-cache,只保存数据的高速缓存被称为d-cache。既保存指令又保存数据的高速缓存被称为统一的高速缓存(unifiied cache)。i-cache通常是只读的。
mSAIB9.jpg

高速缓存的性能评价

评价指标

  • 不命中率(miss rate)。在一个程序执行或程序的一部分执行期间,内存引用不命中的比率。
  • 命中率(hit rate)。命中的内存引用比率
  • 命中时间(hit time)从高速缓存传送一个字到CPU所需的时间。对于L1高速缓存来说,命中时间的数量级是几个时钟周期
  • 不命中处罚(miss penalty)。由于不命中所需要的额外的时间。

影响性能的一些因素

1.高速缓存的大小的影响
一方面较大的高速缓存可能会提高命中率,另一方面,使较大存储器运行得更快总是更难一些
2.块大小得影响
较大得块能够利用程序中可能存在得空间局部性,帮助提高命中率。但对于给定得高速缓存大小,块越大就意味着高速缓存行数越少,这会损坏时间局部性比空间局部性更好得程序中的命中率。较大的块对 对不命中的处罚也有负面影响,因为块越大,传送时间就越长。
3.相联度的影响
较高的相联度(也就是说每个组中有更多的行)的优点使降低了高速缓存由于冲突不命中而出现抖动的可能性。但较高的相联度会带来更高的硬件成本和命中时间,同时也会增加bu'min'zho
4.写策略的影响
直写高速缓存比较容易实现,而且能够使用独立于高速缓存的写缓冲区,用来更新内存。

什么是链接

链接(linking)使将各种代码和数据片段收集并组后成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时、加载时,she连接器在软件开发中扮演着一个关键的角色,它使得分离编译成为可能。
mS0jxK.png

静态链接

为了钩爪可执行文件,链接器必须完成两个主要任务:
1.符号解析(symbol resolution)。目标文件定义和引用的符号,每个符号对应于一个函数,一个全局兵力或一个静态变量,符号解析的目低时间每个符号引用正好和一个符号定义关联起来。
2.重定位(relocation)。编译器和汇编器生成从地址0开始的代码和数据节。连接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所对应这些符号的引用,使得他们指向这个内存位置。

链接器是如何解析多重定义的全局符号

初始化的全局变量和函数名是强符号
Linux连接器使用以下的规则来处理多重定义的符号名
1.不允许出现多个同名的强符号
2.如果有一个强符号和弱符号同名,那么选择强符号
2.入股有多个弱符号同名,那么从这个弱符号中任选一个。

与静态库链接

所有的编译系统都提供一种机制,间所有相关的目标模块打包为一个单独的文件,称为静态库(static librart),它可以用做连接器的输入。但连接器构造一个输出的可执行文件时,它只是复制静态库里被应用程序引用的目标模块。

动态链接共享库

共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程称为动态链接(dynamic linking)。是由一个叫做动态连接器(dynamic linker)的程序来执行的。共享库也称为共享目标。
共享库以两种方式来实现共享的:
1.在任何给定的系统文件中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个so文件中的代码和数据。
2.在内存中,一个共享库的.text节的一个副本可以被不公的正在运行的进程共享。

位置无关代码

共享库允许多个正在运行的进程共享内存中相同的库代码。现代系统为了提高内存的利用率,使用一种特殊的方式编译共享模块的代码段,使得可以把共享库加载到内存的任何位置而无需链接器修改。使用这种方法,使得无限多的进程可以共享一个共享模块的代码段的单一副本。
可以加载而无需重定位的代码称为位置无关代码(Position-Independent Code,PIC)。

PIC数据引用

编译器让共享库的数据段和代码段的距离保持不变。因此,代码段中任何指令和数据段中的任何变量之间的距离都是运行时常量,与代码段和数据段的绝对内存位置是无关的。
编译器在数据段开始的地方创建了一个表,叫做全局偏移量表(Global Offset Table,GOT)。在加载时,动态连接器会重定位GOT中的每个条目,使得它包含目标的正确的绝对地址。每个引用全局目标的目标模块都有自己的GOT。
mFzbLQ.png

PIC函数调用

GNU编译系统使用延迟绑定(lazy binding)的技术,将过程地址的绑定延迟到第一次调用这个过程时,使用这种方式,可以解决编译器无法预测共享库中过程的运行时地址的问题。使用延迟绑定技术还可以提供效率,让那些在未被使用的过程不进行绑定从而提高效率。
延迟绑定是借助GOT和过程链接表(Procedure Linkage Table,PLT)来实现的。如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的GOT和PLT。GOT是数据段的一部分,而PLT是代码段的一部分。
mA2gsO.png

库打桩机制

Linux连接器支持库打桩(library interpositioning)。它允许截获对共享库函数的调用,取而代之执行自己的代码。
它的基本思想:给定一个需要打桩的目标函数,创建一个包装函数,它的原型和目标函数完全一样。使用某种特殊的打桩机制欺骗系统调用包装函数而不是目标函数了。包装函数通常会执行它自己的逻辑,然后调用目标函数,在将目标函数的返回值传递给调用者。
库打桩机制可以在:编译时、链接时、运行时。

原文地址:https://www.cnblogs.com/zofun/p/11443921.html