【发布时间】:2010-12-29 10:04:08
【问题描述】:
动态库的链接和加载是否都发生在运行时? 还是仅在运行时加载库?
【问题讨论】:
-
这不是完全重复的
标签: c compilation
动态库的链接和加载是否都发生在运行时? 还是仅在运行时加载库?
【问题讨论】:
标签: c compilation
请参阅前面关于静态链接和动态链接之间区别的非常好的观点。假设您指的是动态链接,那么:
加载和(动态)链接都是由链接器完成的——在 linux 和其他类似 Unix 的系统上,这是由 /lib/ld.so 完成的,这是操作系统在几乎所有情况下启动的实际程序。 ld.so 依次加载您的应用程序 - mygameBinary 到内存中,然后ld.so 从文件 mygameBinary 中读取它需要的动态链接库列表。
链接器ld.so 然后将这些库中的每一个依次加载到内存中,例如libc.so、libpthread.so、libopengl.so,并查看 这些 可能需要的其他库,例如libm.so.
加载完成后,链接就开始了,这是一个查看命名对象或函数的过程,这些对象或函数导出由一个库或应用程序,并由另一个库或应用程序导入。然后,链接器会更改各种引用,有时还会更改代码以更新每个库中未链接的数据指针和函数调用,以指向实际数据或函数所在的位置。例如,在mygameBinary 中对printf 的调用一开始没有指向任何东西(实际上它只是调用链接器),但在链接之后变成了跳转到libc 中的printf 函数。
此链接完成后,通过调用mygameBinary 中的_start 函数启动应用程序,然后调用main,然后您的游戏开始。
这种方式的动态链接对于支持以下内容是必要的:
一些操作系统在细节上有所不同,例如 OSX 和 AIX 都将一组特定的库预加载到内存中的固定位置。这意味着它们不需要加载,只需链接,这可能会更快。
某些操作系统(例如 OSX,有时甚至是 Linux)支持预链接,这是一个脚本在您启动系统上的应用程序之前运行它们并执行链接的过程。当您启动它们时,您不需要链接它们。这一点很重要,因为当您启动应用程序时,链接会占用您计算机的大量时间,并且某些应用程序可能会在应用程序构建过程中每秒启动多次,例如gcc、cpp 和as 在应用程序构建过程中或过滤器索引计算机数据时的脚本 (OSX Spotlight)。
【讨论】:
链接是获取一些较小的可执行文件并将它们连接在一起作为单个较大的可执行文件的过程。
加载是在执行之前将可执行文件加载到内存中。
【讨论】:
有两种类型的链接:静态链接和动态链接。
静态链接发生在编译时,因此它发生在加载程序之前。通过静态链接,程序使用的外部符号(例如函数名)在编译时解析。
动态链接发生在运行时,因此它发生在程序加载之后或加载时。使用动态链接,符号在加载时或在运行时访问符号时解析(惰性绑定)。后者更常见。
【讨论】:
file01.c, file02.c --> 产生 --> file01.o, file02.o --> 这些.o信息被合并并放入一个单独的动态库,即lib1.a file11.c, file12.c --> 生产 --> file11.o, file12.o --> 这些.o信息被合并并放入一个单独的动态库,即lib2.a
现在,我有 2 个库最终链接在一起以生成可执行文件(如 .elf 或 .mot 或 .fls)。链接来自 lib1.a 和 lib2.a 的信息以形成单个可执行文件的过程称为链接。
现在,我需要将它加载到内存中以便运行它以查看最终可执行文件的行为。将最终的可执行文件(如 .elf 或 .mot 或 .fls)加载到内存中以便运行的过程称为加载。
我希望这将清楚链接和加载的重要性(但是定义不合适:-))。
【讨论】:
对于动态库,两者都发生在运行时。
首先,加载库及其所有依赖项(以及这些库的依赖项,等等)。然后动态链接器解析加载的库中的符号。通常这两个功能都是由同一个软件实现的;在 Linux 上是 ld.so。
静态库中的符号在链接时解析并包含在可执行文件本身中。但是,静态库可能具有在运行时由动态库满足的未解析符号。
在How to Write Shared Libraries 中有深入描述这是如何发生的、如何对名称进行哈希处理、在运行时解析符号的成本等。
【讨论】:
Windows 和 Unix 系统对动态库使用完全不同的方法。
Windows DLL 未链接。因此,您不能跨 DLL 共享静态对象。它就像你的地址空间中的一个单独的程序。
Unix 共享对象在运行时实际上是“链接”的,就像同一个项目的不同模块执行符号解析一样。
【讨论】:
动态链接和库加载都发生在运行时,但动态链接是在程序执行之前完成的,由系统链接器完成。因此,例如,如果缺少所需的库,则无法执行程序。另一方面,库加载是由程序本身通过 dlopen/LoadLibrary 函数完成的。在这种情况下,加载过程由应用程序控制,例如可以处理错误。
【讨论】: