动态链接的工作原理是什么?
在Windows(使用LoadLibrary),你需要在运行时调用一个.dll文件,但在链接时,你需要提供相应的.lib文件,否则程序无法链接......那么.lib文件包含什么呢?.dll方法的描述吗?这不是头文件中包含的吗?
类似地,在*nix上,你不需要lib文件......那么编译器如何知道头文件中描述的方法在运行时将可用?
作为一名新手,当你考虑其中任何一种方案时,再考虑另一种方式,两种方案都没有意义......
动态链接的工作原理是什么?
在Windows(使用LoadLibrary),你需要在运行时调用一个.dll文件,但在链接时,你需要提供相应的.lib文件,否则程序无法链接......那么.lib文件包含什么呢?.dll方法的描述吗?这不是头文件中包含的吗?
类似地,在*nix上,你不需要lib文件......那么编译器如何知道头文件中描述的方法在运行时将可用?
作为一名新手,当你考虑其中任何一种方案时,再考虑另一种方式,两种方案都没有意义......
LoadLibrary
或dlopen
,然后为每个需要使用的符号调用GetProcAddress
/dlsym
。隐式加载用于像系统库这样的东西,其中实现将取决于系统的版本,但接口是有保证的。显式加载用于像插件这样的东西,其中要加载的库将在运行时确定。
.lib
文件仅对隐式加载必要。它包含库实际提供此符号的信息,因此链接器不会抱怨符号未定义,并告诉链接器在哪个库中找到这些符号,以便它可以插入必要的信息来自动加载此库。所有的头文件告诉编译器的只是符号将存在于某个地方;链接器需要.lib
来知道在哪里。.so
中提取。为什么Windows需要两个单独的文件而不是把所有信息放在一个文件中,我不知道;实际上,这是重复大部分信息,因为.lib
中所需的信息在.dll
中也是必需的。(也许是许可问题。您可以使用.dll
分发程序,但除非他们有一个.lib
,否则没有人可以链接到库。).lib
还是.so
文件,以便它可以将该信息插入可执行文件中。如果要进行显式加载,则必须手动加载和解析库,并对要使用的每个符号调用相应函数。加载库后,您无法直接引用库中的任何符号;您必须调用GetProcAddress
/dlsym
来获取它们的地址(并进行一些有趣的类型转换才能使用它们)。.lib
文件也可以用于静态链接可能会很有帮助。静态链接将库的内容链接到可执行文件中,从而不需要使用DLL。 - Excelcius.lib
文件不需要包含任何编译代码。 - Excelcius.lib
文件不是加载动态库所必需的,它只是提供了一种方便的方式。
原则上,您可以使用LoadLibrary
来加载dll文件,然后使用GetProcAddress
来访问该dll提供的函数。在这种情况下,封装程序的编译不需要访问dll,它仅在运行时(即LoadLibrary
实际执行时)需要。MSDN有一个代码示例。
这里的缺点是您需要手动编写从dll中加载函数的代码。如果您首先编译了dll,则此代码仅重复了编译器可以自动从dll源代码中提取的知识(如导出函数的名称和签名)。
这就是.lib
文件的作用:它包含由编译器生成的 Dll 函数的 GetProcAddress
调用,因此您不必担心。从 Windows 角度来看,这被称为加载时动态链接,因为当加载您的封闭程序时,.lib 文件中的代码会自动加载 Dll(与手动方法相对,称为运行时动态链接)。SYMBOL ADDRESS TYPE SIZE
my_function 1000 function 2893
my_number 4800 variable 4
让操作系统在大约同时(在执行main()
之前)加载自身和共享对象,由操作系统加载程序文件映像中有关这些符号使用的元数据并查找符号,然后在程序使用的内存中插入符号地址,使得程序可以正常运行,就好像在它第一次编译时已经知道了符号地址(但可能会慢一些)。
或者,在其自己的源代码中明确调用dlopen
,然后使用dlsym
或类似方法获取符号地址,根据程序员对预期数据类型的了解将它们保存到(函数/数据)指针中,然后使用指针显式调用它们。
在Windows(LoadLibrary)中,你需要一个.dll文件在运行时调用,但在链接时,你需要提供一个相应的.lib文件,否则程序将无法链接...
这听起来不对。我认为应该是其中之一。
那个.lib文件包含什么?.dll方法的描述?这不是头文件包含的内容吗?
在这个级别上,lib文件与共享对象文件基本相同...主要区别在于编译器在程序发货和运行之前找到符号地址。
.lib
可以是静态库(此时没有.dll
),也可以是一些来自.dll
的信息的集合,由链接器用于生成必要的自动库加载信息。 (在Unix下,链接器直接从.so
文件中提取此信息。) - James KanzeLoadLibrary
,则不需要相应的 .lib 文件。我曾经使用 LoadLibrary
,正是因为我的开发系统没有 .lib 文件,尽管目标系统有 .dll 文件。 - Adrian RatnapalaLoadLibrary
,并通过名称找到要调用的函数,并且它们实际起作用,那么DLL中有足够的信息自动构建导入库。 您的IDE应该包括一个执行此操作的工具。VS有,我使用Borland时也有。 - cHao相关的是,在OS X上(我认为*nix... dlopen也是如此),您不需要一个lib文件...编译器如何知道在运行时可用的头文件中所描述的方法?
编译器或链接器不需要这样的信息。作为程序员,您需要处理尝试通过dlopen()
打开的共享库可能不存在的情况。
.so
范例是由Sun在大约1980年建立的。它从来没有真正是秘密,所以微软在实现动态加载时本可以使用它,大约20年后。我只是猜测,但我认为这是许可问题所在(但这可以通过静态链接更好地解决)。 - James KanzeLoadLibrary
。.so
/.dynlib
文件)并找出所需内容,而无需像Windows那样使用单独的静态库。-lSDL
来链接到SDL动态库)b) 在运行时加载它(例如:将此代码放入您的项目中:void* lib=dlopen("./libSDL.so", RTLD_LAZY); //现在通过dlsym导入函数指针...
)。在**a)中,当开发人员机器上的libSDL.so
在链接时不可用时,您将收到链接错误。在b)**中,只有当SDL在_end-user_的当前目录中不可用时,您才会收到运行时错误。 - cubuspl42.lib
文件中的内容直接包含在Linux/OS X上的.so
/.dynlib
中。但是主要问题是...为什么呢?
*nix解决方案不是更好吗?
我认为是的,但是.lib
有一个优点。链接到DLL的开发人员实际上不需要访问DLL文件本身。
这种情况在现实世界中经常发生吗?维护每个DLL文件的两个文件值得吗?我不知道。
编辑:好的,伙计们让事情变得更加混乱!您可以使用MinGW在Windows上直接链接到DLL。因此,整个导入库问题与Windows本身不是直接相关的。摘自MinGW wiki上的sampleDLL文章:
由"--out-implib"链接器选项创建的导入库是 当且仅当从除MinGW工具链以外的某些C/C++编译器接口化DLL时才需要的。MinGW工具链是 完全可以直接链接到创建的DLL。更多详细信息 可以在binutils中作为一部分的ld.exe信息文件中找到 包(这是工具链的一部分)。
.dll
。可能在链接时有轻微的优势,因为.lib
通常比 .dll
小得多。(仍然需要 .dll
进行开发,因为您必须运行刚刚链接的程序以进行测试。) - James Kanze.a
文件可以包含任何东西(“a” 代表“归档”),但通常包含 .o
文件。 - cHaoLinux同样需要链接,但是不像Windows那样链接到.Lib库,而是需要链接到动态链接器/lib/ld-linux.so.2
,但是当使用GCC时,这通常是在后台自动完成的(但是如果使用汇编程序,则需要手动指定)。
两种方法,无论是Windows的.LIB方法还是Linux的动态链接器链接方法,在现实中都被认为是静态链接。但是有一个区别,在Windows中,部分工作是在链接时完成的,虽然它仍然需要在加载时进行一些工作(我不确定,但我认为.LIB文件仅用于让链接器知道物理库名称,符号只在加载时解析),而在Linux中,除了链接到动态链接器之外,所有工作都发生在加载时。
动态链接通常是指在运行时手动打开DLL文件(例如使用LoadLibrary()),在这种情况下,负担完全在程序员身上。
.dll
.dylib
和.so
,有一些关于符号名称和地址的信息,如下所示:------------------------------------
| symbol's name | symbol's address |
|----------------------------------|
| Foo | 0x12341234 |
| Bar | 0xabcdabcd |
------------------------------------
而加载函数,例如LoadLibrary
和dlopen
,会加载共享库并使其可用于使用。
GetProcAddress
和dlsym
会找到您符号的地址。例如:
HMODULE shared_lib = LoadLibrary("asdf.dll");
void *symbol = GetProcAddress("Foo");
// symbol is 0x12341234
.lib
文件用于使用.dll
。当您链接到此.lib
文件时,您无需调用LoadLibrary
和GetProcAddress
,只需像使用“普通”函数一样使用共享库的函数即可。它是如何工作的?.lib
包含一个导入信息。就像这样:void *Foo; // please put the address of Foo there
void *Bar; // please put the address of Bar there
当操作系统加载您的程序(严格来说,是您的模块)时,操作系统会自动执行LoadLibrary
和GetProcAddress
。
如果您编写了Foo();
这样的代码,编译器会自动将其转换为(*Foo)();
。因此,您可以像使用“普通”函数一样使用它们。
dlopen
的调用稍微快一点。 - cubuspl42Foo()
翻译成 (*Foo)()
。加载器也不会,因为你指出了,没有指针。粗略地说:编译器将在目标文件中插入一个 Foo
的占位符,链接器将用实际地址填充它。静态链接和动态链接之间的区别在于这种填充发生的时间。 - James Kanze
LoadLibrary
加载 DLL,这就是“动态加载”库的优点。其他平台也是如此。 - Some programmer dude