“静态链接”和“动态链接”是什么意思?

时间:2008-11-22 23:09:57

标签: c# c++ linker static-linking dynamic-linking

我经常听到“静态链接”和“动态关联”这两个词,通常是指用CC++C#编写的代码。它们是什么,它们到底在说什么,它们之间有什么联系?

5 个答案:

答案 0 :(得分:403)

从源代码(您编写的内容)到可执行代码(您运行的内容)的两个阶段(在大多数情况下,折扣解释代码)。

第一个是将源代码转换为对象模块的编译。

第二个是链接,它将对象模块组合在一起形成可执行文件。

除其他外,区别在于,允许第三方库包含在您的可执行文件中,而无需查看其源代码(例如用于数据库访问的库,网络通信和图形用户界面),或者用于编译代码。不同的语言(例如C和汇编代码),然后将它们全部链接在一起。

静态将文件链接到可执行文件时,该文件的内容将包含在链接时。换句话说,文件的内容将物理插入到您将运行的可执行文件中。

当您动态链接 时,指向要链接的文件的指针(例如,文件的文件名)包含在可执行文件中,并且所述文件的内容不包含在链接中时间。只有当您稍后运行可执行文件时才会购买这些动态链接文件,并且它们只会被购买到可执行文件的内存副本,而不是磁盘上的副本。

它基本上是一种延迟链接的方法。有一个甚至更多延迟方法(在某些系统上称为后期绑定),在您实际尝试调用其中的函数之前,它不会引入动态链接文件。

静态链接文件在链接时被“锁定”到可执行文件,因此它们永远不会更改。可执行文件引用的动态链接文件只需更换磁盘上的文件即可更改。

这允许更新功能而无需重新链接代码;每次运行时,加载程序都会重新链接。

这既好又坏 - 一方面,它允许更容易的更新和错误修复,另一方面它可以导致程序停止工作,如果更新不兼容 - 这有时负责可怕的“DLL地狱”如果你用一个不兼容的库替换一个动态链接库,有些人会提到应用程序可能会被破坏(顺便说一句,这样做的开发人员应该被追捕并严厉惩罚)。


作为示例,让我们看一下用户编译main.c文件以进行静态和动态链接的情况。

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

您可以在静态情况下看到主程序和C运行时库在链接时链接在一起(由开发人员)。由于用户通常无法重新链接可执行文件,因此他们会遇到库的行为。

在动态情况下,主程序与C运行时导入库链接(某些东西声明了动态库中的内容,但实际上并未定义它)。即使实际代码丢失,这也允许链接器链接。

然后,在运行时,操作系统加载程序将主程序与C运行时DLL(动态链接库或共享库或其他命名法)进行后期链接。

C运行时的所有者可以随时删除新的DLL以提供更新或错误修复。如前所述,这有利有弊。

答案 1 :(得分:210)

我认为这个问题的一个好答案应该解释的链接。

编译某些C代码(例如)时,它会被翻译成机器语言。只是一个字节序列,当运行时,会导致处理器添加,减去,比较,“转到”,读取内存,写入内存等等。这些东西存储在对象(.o)文件中。

现在,很久以前,计算机科学家发明了这种“子程序”的东西。执行 - 这 - 块-的代码和回报 - 在这里。不久之后,他们意识到最有用的子程序可以存储在一个特殊的地方,并被需要它们的任何程序使用。

现在,在早期,程序员必须打入这些子程序所在的内存地址。像CALL 0x5A62这样的东西。如果需要更改这些内存地址,这是繁琐且有问题的。

因此,该过程是自动化的。您编写了一个调用printf()的程序,编译器不知道printf的内存地址。所以编译器只写CALL 0x0000,并在对象文件中添加一条注释,说“必须用 printf ”的内存位置替换这个0x0000。

静态链接意味着链接器程序(GNU称为ld)将printf的机器代码直接添加到可执行文件中,并将0x0000更改为{{1}的地址}。创建可执行文件时会发生这种情况。

动态链接意味着上述步骤不会发生。可执行文件仍然有一个注释,表示“必须用printf的内存位置替换0x000”。操作系统的加载程序需要找到printf代码,将其加载到内存中,并在每次运行程序时更正CALL地址

程序通常会调用一些静态链接的函数(标准库函数,如printf通常是静态链接的)和其他动态链接的函数。当可执行文件运行时,静态的“成为可执行文件的一部分”,动态的“加入”。

这两种方法都有优点和缺点,操作系统之间也存在差异。但既然你没有问,我会在这里结束。

答案 2 :(得分:29)

静态链接库在编译时链接。动态链接库在运行时加载。静态链接将库位烘焙到您的可执行文件中。动态链接仅烘焙对库的引用;动态库的位存在于别处,可以在以后换出。

答案 3 :(得分:12)

因为以上帖子中没有一个实际显示如何静态链接某些东西,并且看到你正确地做了这些,所以我将解决这个问题:

一个简单的C程序

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

动态链接C程序

gcc simpleprog.c -o simpleprog

在二进制文件上运行file

file simpleprog 

这将表明它是动态链接的:

“simpleprog:ELF 64位LSB可执行文件,x86-64,版本1(SYSV),动态链接(使用共享库),用于GNU / Linux 2.6.26,BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f,未剥离”< / p>

相反,让我们这次静态链接程序:

gcc simpleprog.c -static -o simpleprog

在此静态链接的二进制文件上运行文件将显示:

file simpleprog 

“simpleprog:ELF 64位LSB可执行文件,x86-64,版本1(GNU / Linux),静态链接,用于GNU / Linux 2.6.26,BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b,未剥离”

你可以看到它与静静地联系在一起。遗憾的是,并非所有库都很容易以这种方式静态链接,并且可能需要使用libtool进行扩展工作或手动链接目标代码和C库。

幸运的是,许多嵌入式C库(如musl)为几乎所有(如果不是全部)库提供了静态链接选项。

现在strace您创建的二进制文件,您可以看到在程序开始之前没有访问过的库:

strace ./simpleprog

现在与动态链接程序中strace的输出进行比较,你会发现静态链接版本的strace要短得多!

答案 4 :(得分:2)

(我不知道C#,但对于VM语言有一个静态链接概念很有趣)

动态链接涉及了解如何查找所需的功能,您只能从程序中获得参考。您可以在语言运行时或OS上搜索文件系统,网络或编译代码缓存上的一段代码,匹配引用,然后采取多种措施将其集成到内存中的程序映像,如重定位。它们都是在运行时完成的。它可以手动完成,也可以由编译器完成。有能力更新,有可能搞乱(即DLL地狱)。

静态链接在编译时完成,您告诉编译器所有功能部件的位置并指示它集成它们。没有搜索,没有歧义,没有重新编译就无法更新。您的所有依赖项都与您的程序映像完全相同。