我听到过关于包装的“动态链接”和“静态链接”这两个术语,但我一直不清楚这些术语的实际含义。
什么是动态链接和静态链接?
答案1
这个问题其实应该分为两个问题:“什么是动态链接和静态链接?”以及“Click 如何解决依赖关系问题?”。这两个问题没有关系。这里我尝试回答第一个问题。
什么是动态链接和静态链接?
任何软件程序的源代码都会使用外部函数。外部函数驻留在库中。将程序编译为机器代码*时,外部引用必须以某种方式链接到它们的定义,即它们在机器代码中的实现。有两种方法可以做到这一点。要么 (a) 将实现代码从库中“拉入”并添加到生成的二进制文件中,要么 (b) 将引用悬空并将“指向”它们的实现运行。我们称之为 (a) 静态链接和 (b) 动态链接。
与任何工程决策一样,没有哪种选择比另一种更好。静态链接的优点是不会对运行时环境产生依赖,因此会生成更可预测的二进制文件。但代价是代码重复,从而导致磁盘、网络和内存消耗。动态链接允许在运行时共享库代码。它还允许升级库(例如,当发现错误时),而无需重新编译使用它们的二进制文件。所有依赖项都会自动修复。显然,它们也会因新版本引入的错误和不兼容性而自动损坏。
抛开利弊不谈,动态链接本身就产生了更复杂的系统。有更多移动部件,尤其是移动部件之间存在依赖关系**。那么问题就是如何管理这种复杂性。例如,如何在系统上拥有一个应用程序的两个版本,每个版本都需要自己的依赖项,其中可能有一个库的两个不同的次要版本。由于这些库的文件名发生冲突,因此目前在 Ubuntu 上无法实现这一点。
可以说,每个打包系统除了简化软件安装之外,都试图解决依赖性和版本控制问题,同时获得动态依赖性的好处。Debian 打包系统在出现时在这方面比现有的打包系统要好得多。从那时起,许多其他解决方案也应运而生。一个特别彻底的方法是由GNU 指南和它的Guix 包管理器。点击是另一个。
*) 我们假设程序被编译为机器代码,而不是被解释或编译为中间语言(例如 Python、Java)的程序。从抽象层面上讲,这些语言遭受着完全相同的动态解析问题(ClassNotFoundException
在 Java 中),但将它们包括在这里只会使解释变得混乱。
**) 请注意,如果您坚持使用静态链接的二进制文件,那么就不会有运行时相互依赖关系。也就是说,没有图书馆依赖关系,还有其他类型。