2019年存储空间分配.doc

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第4章存储空间分配

$Revision: 2.3 $

$Date: 1999/06/15 03:30:36 $

链接器或加载器的首要任务是存储分配.一旦分配了存储空间后,链接器就可以继续

进行符号绑定和代码调整.在一个可链接目标文件中定义的多数符号都是相对于文件内的存储区域定义的,所以只有存储区域确定了才能够进行符号解析.

与链接的其它方面情况相似,存储分配的基本问题是很简单的,但处理计算机体系结

构和编程语言语义特性的细节让问题复杂起来.存储分配的大多数工作都可以通过优雅和相对架构无关的方法来处理,但总有一些细节需要特定机器的专门技巧来解决.

段和地址

每个目标或可执行文件都会采用目标地址空间的某种模式.通常这里的目标是目标计

算机的应用程序地址空间,但某些情况下(例如共享库)也会是其它东西.在一个重定位链

接器或加载器中的基本问题是要确保程序中的所有段都被定义并具有地址,并且这些地址不能发生重叠(除非有意这样).

每一个链接器输入文件都包含一系列各种类型的段.不同类型的段以不同的方式来处

理.通常,所有相同类型的段,诸如可执行代码段,会在输出文件中被合并为一个段.有时

候段是在其它段的基础上合并得到的(如Fortran的公共块),以及在越来越多的情况下

(如共享库和C++专有特性),链接器本身会创建一些段并将其放置在输出中.

存储布局是一个"两遍"的过程,这是因为每个段的地址在所有其它段的大小未确定

前是无法分配的.

简单的存储布局

在一种简单而不现实的情形下,链接器的输入文件包含一系列的模块,将它们称为M1,

M2, ... Mn,每一个模块都包含一个单独的段,从位置0开始长度依次为L1, L2, ... Ln,并

且目标地址空间也是从0开始.如图1所示.

---------------------------------------------------------------------------

图4-1:单独段的存储空间分配

从位置0开始的多个段按照一个跟着另一个的方式重定位

---------------------------------------------------------------------------

链接器或加载器依次检查各个模块,按顺序分配存储空间.模块Mi的起始地址为从L1

到Li-1相加的总和,链接得到的程序长度为从L1到Ln相加的总和.

多数体系结构要求数据必须对齐于字边界,或至少在对齐时运行速度会更快些.因此

链接器通常会将Li扩充到目标体系结构最严格的对齐边界(通常是4或8个字节)的倍数. 例1:假定一个称为main的主程序要与三个分别称为calif,mass和newyork的子例程

链接(按照地理位置划分风险投资).每个例程的大小为(16进制数字):

名称尺寸

-------------------

ain1017

calif 920

ass 615

newyork1390

假定从16进制的地址1000处开始分配存储空间,并且要求4字节对齐,那么存储分配

的结果可能是:

名称位置

-------------------------

ain1000 - 2016

calif2018 - 2937

ass2938 - 2f4c

newyork2f50 - 42df

由于对齐的原因,2017处的一个字节和2f4d处的三个字节被浪费了,但无须忧虑.

多种段类型

除最简单格式外所有的目标格式,都具有多种段的类型,链接器需要将所有输入模块

中相应的段组合在一起.在具有文本和数据段的UNIX系统上,被链接的文件需要将所有的文本段都集中在一起,然后跟着的是所有的数据,在后面是逻辑上的BSS(即使BSS在输出文件中不占空间,它仍然需要分配空间来解析BSS符号,并指明当输出文件被加载时要分配的BSS空间尺寸).这就需要两级存储分配策略.

现在每一个模块Mi具有大小为Ti的文本段,大小为Di的数据段,以及大小为Bi的BSS 段,如图2所示.

---------------------------------------------------------------------------

图4-2:多种段的存储分配

按类型将文本,数据和BSS段分别归并

---------------------------------------------------------------------------

在读入每个输入模块时,链接器为每个Ti,Di,Bi按照(就像是)每个段都各自从位置

0处开始的方式分配空间.在读入了所有的输入文件后,链接器就可以知道这三种段各自总的大小Ttot,Dtot和Btot.由于数据段跟在文本段之后,链接器将Ttot加到每一个数据段所分配的地址上,接着,由于BSS跟在文本和数据段之后,所以链接器会将Ttot,Dtot的和加到每一个BSS段分配的地址上.

同样,链接器通常会将分配的大小按照对齐要求扩充补齐.

段与页面的对齐

如果文本和数据被加载到独立的内存页中,这也是通常的情况,文本段的大小必须扩

充为一个整页,相应的数据和BSS段的位置也要进行调整.很多UNIX系统都使用一种技巧来节省文件空间,即在目标文件中数据紧跟在文本的后面,并将那个(文本和数据共存的)

页在虚拟内存中映射两次,一次是只读的文本段,一次是写时复制(copy-on-write)的数

据段.这种情况下,数据段在逻辑上起始于文本段末尾紧接着的下一页,这样就不需扩充文本段,数据段也可对齐于紧接着文本段后的4K(或者其它的页尺寸)页边界.

例2:我们将例1扩展,使得每个例程都有文本,数据和BSS段.字对齐要求还是4个

字节,但页大小为0x1000字节.

名称文本段数据段BSS段

-------------------------------------------------------------

ain1017 32050

calif 920 217100

ass 615 300840

newyork139012131400

(均为16进制数字)

链接器首先分配文本段,然后是数据段,接着是BSS.注意这里数据段起始于页边界0x 5000,但BSS紧跟在数据的后面,这是因为在运行时数据和BSS在逻辑上是一个段.

名称文本段数据段BSS段

-------------------------------------------------------------

ain1000-20165000-531f695c-69ab

相关文档
最新文档