Win32动态连接库基址重置技术

类别:VC语言 点击:0 评论:0 推荐:
理论

要加载动态链接库操作系统必须完成以下各步:

在磁盘上定位动态链接库可执行文件。 仔细查看已经加载进应用程序地址空间中的动态链接库列表,判断动态链接库是否已经加载了。 为动态链接库分配驻留内存,并将动态链接库二进制文件映射到内存中(在Windows NT中,映射跨越了段对象)。 为使动态链接库正常运行,执行一系列必要的处理(例如,解析动态链接库中做过的修正,等等)。

不同的参数决定了动态链接库的加载时间不同。以下给出了需要考虑的各种因素,事实上可能还有一些因素也会影响到动态链接库的加载时间:

底层软硬件:计算机本身的速度以及运行哪一种操作系统。 当前系统和应用程序的状态:在虚拟内存中系统的紧密情况,以及动态链接库是否可以被加载在首选基地址。 动态链接库本身:动态链接库本身有多大,动态链接库中有多少位置需要修正(结合两者综合考虑),此动态链接库是否隐式链接到另一个同样需要加载的动态链接库中。

由上述分析可知,对某一动态链接库进行基址重置绝不是影响动态链接库加载时间的唯一决定因素。在本文中,作者使用了很多数据,说明了动态链接库加载时间的变化范围以及应用程序可能对加载时间的影响程度。

读者应该注意到,对某一动态链接库进行基址重置,不仅可能造成加载时间的大幅度增加,还需要增加页文件(pagefile)的开销。加载动态链接库的第一步,需要创建区段对象(section object),区段对象是由动态链接库可执行文件支持的、内存中的一个相邻的区段。一旦动态链接库的某一页被从应用程序工作集中移去了,在下一次访问此页时,操作系统就会从动态链接库可执行文件中重新加载这一页。

当然,当动态链接库进行基址重置时,这一策略不再起作用,这主要是因为包含重定位地址的页与动态链接库可执行文件映射中的相应页不同。因此,一旦在加载可执行文件时,操作系统企图修正地址,就会拷贝相应的页(由于区段是通过COPY_ON_WRITE标志打开的)。在拷贝中进行的所有的修改,操作系统都会记住,从现在开始,页将在系统页文件中换入换出,而不再在可执行映象中进行页交换。

采用这种机制,潜在的性能命中有两种:首先,每个包含需要重定位地址的页都占用一页系统页文件(其结果是,减少了所有应用程序可用的虚拟内存数);另外,由于操作系统执行动态链接库页中的第一次修正操作,新的页必须从页文件中分配,并将整个页拷贝下来。

尽管扫描动态链接库重定位区段以及进行内存修正的算法都有很高的效率,执行修正操作还是会增加动态链接库的加载时间。(跨段操作的复杂性是需要修复地址数量的线性函数。)

地址修正

关于动态链接库基址重置的一类经常问到的问题是,“地址修正究竟是什么含义?程序员是否有办法调整代码,以减少可执行程序中的地址修正?”对于这两个问题的回答是,这一切在很大程度上依赖于可执行程序建立在哪一种平台上。在本文中,我们将平台限制在Intel 386,、486、和奔腾(Pentium)处理器上,讨论相应的可执行程序。(注意:为其他平台建立的可执行程序,相应的地址修复概念与本文讨论的概念不同。)

在386、486、或奔腾处理器上,两种情况可能导致某地址被标识为“可重定位”的状态:一是静态对象(static objects),另一种情况是绝对跳转(absolute jumps)。

首先,如果动态链接库引用了静态对象,就会使用对象的绝对地址(假设动态链接库被加载到首选地址中)。例如,在如下的代码段中:

LPSTR lpName="Name";

动态链接库的载入程序就会将“Name”字串分配到动态链接库数据段中,并将此字串的起始地址填写到lpName变量对应的位置中去。如果由于DLL不能被加载到基地址而使得“Name”字串必须重定位,lpName必须相应地进行调整。注意,在这种情况下,代码段中每个引用lpName的变量也必须作相应的地址修正。

可以重定位的对象包括文字字串(例如,在上例中的“Name”字串)、任何一种类型的全局或静态数据、包含静态分配的C++对象。应该注意到,特别是在C++中,可能存在着许多从一个静态对象到另一个静态对象的交叉索引。未初始化的数据不需要在重定位过程中修正地址,但指向未初始化静态数据的索引需要进行地址修正。

在i386可执行代码中,另一类可以进行重定位的项是绝对跳转和函数调用,包括系统函数调用。注意到程序开发人员很难通过修改程序代码来避免地址重定位,唯一可以采用的办法就是缩减静态分配数据的数目。要缩减静态分配数据,一种办法就是尽量避免使用名字进行资源索引,而应该通过坐标进行资源索引(因为,程序员在代码中显式使用的每一个名字都会自动变成一个可重定位的项)。

尽管如此,作者并不建议程序开发人员带着减少加载时间这一特殊目的开发动态链接库代码,除非存在以下两种情况:(1)静态分配对象的数据可以大幅度减少;(2)程序员依据此种方式进行编程时,不会影响其他编程中需要考虑到的因素。

除此之外,程序开发人员还可以通过简单的优化方法,减少加载时间。例如,可以将所有可以重新定位的数据集中到几页中。很显然,如果动态链接库需要进行基址重置,每页含有一个重定位项的两页,都需要有页文件来支持。如果所有的重定位项都出现在同一页中,只有一页需要页文件来支持,因此只有一页会受到影响。在必要的情况下,读者可以使用pragma (data_seg,数据段)伪指令,确保尽可能多的可重定位项被分配到尽可能少的页中去。

本文地址:http://com.8s8s.com/it/it3552.htm