文章目录
一、程序的构建过程
程序编写与编译
编译是将人类可读的源代码转换成机器可执行的代码的过程。这个过程通常由编译器完成。
#include <stdio.h>
int main() {
printf("Hello, World!");
return 0;
}
当上述代码通过编译器编译时,编译器首先将源代码翻译成汇编语言,然后进一步转换成机器代码。机器代码通常是指令集架构(ISA)指定的操作码和操作数的组合,它们可以被特定类型的CPU直接执行。
程序的装入
程序装入是指将程序代码和数据从磁盘装入到内存中,以便CPU可以执行这些指令的过程。这一步骤对于程序的运行来说至关重要,因为CPU不能直接从磁盘执行指令,所有的执行动作都是在内存中进行的。
装入可以是静态的,即在程序开始执行前一次性完成;也可以是动态的,即在程序运行时根据需要加载不同部分到内存中。
程序的链接
链接是在编译之后,装入之前发生的过程,它负责将各个编译后的代码和库文件合并成一个单一的可执行文件。
静态链接
静态链接发生在编译时刻,它会把所有用到的库文件集合到最终的可执行文件中。生成的可执行文件较大,但是不需要在运行时链接额外的库文件。
动态链接
动态链接与静态链接相对,指的是在运行时才将库文件加载到内存中。动态链接库(Dynamic Link Libraries,DLL在Windows系统中或Shared Objects,.so在Unix-like系统中)只在需要时才装入内存,可以被多个程序共享,这样可以节省磁盘空间和内存。
二、程序装入详解
程序装入是指将编译后的程序代码和数据从磁盘传输到内存中的过程。装入的时机和过程对程序的执行效率和资源占用有直接影响。
1.装入时机
静态装入
静态装入发生在程序开始执行之前,通常由操作系统的装入器完成。在这种情况下,程序的装入地址在编译时就已经确定,一旦装入,地址不再改变。
动态装入
动态装入则更加灵活,程序代码在需要执行时才被装入内存。这种方法允许多个程序共享代码和库,节约内存,但是需要操作系统提供更复杂的管理机制来监控程序的行为。
运行时装入
运行时装入是动态装入的特例,其中程序的某些部分可能在程序已经开始执行后才被装入内存。这常常用于那些只有在特定条件下才需要执行的代码,如按需加载的插件或模块。
2.装入过程
绝对装入
绝对装入是最简单的装入方式,编译器产生的是绝对代码,也就是说,目标代码中的所有地址都是物理地址,直接装入内存中指定的位置即可。这种方法的限制是程序必须始终装入到同一位置。
可重定位装入
与绝对装入相对,可重定位装入允许程序在内存中的位置可以变动。在这种方式下,编译器生成的目标代码不是绝对地址,而是相对地址。装入时,装入器会为程序选择内存中的位置,并调整代码和数据中的地址以适应这个位置。
动态运行时装入
动态运行时装入是指程序在运行时,由专门的装入器动态地将程序的模块装入到内存中的指定位置。通常与动态链接结合使用,允许程序只装入当前需要的部分,其他不需要执行的部分可以暂时留在磁盘上。
三、程序链接详解
程序链接负责将程序的各个部分和所需的库整合在一起。链接可以是静态的,也可以是动态的。
1.静态链接
过程
静态链接的过程发生在编译后,执行前。链接器(Linker)会收集程序引用的所有静态库中的对象文件,将它们与程序的目标代码合并,解析所有未定义的符号,最终生成一个完整的可执行文件。
特点
- 独立性: 生成的可执行文件包含了所有必需的代码,不依赖于外部的库文件。
- 性能: 由于在程序启动前就已经解决了所有的引用,因此运行时不需要额外的加载和链接步骤,可以提高执行速度。
- 兼容性: 减少了由于库更新导致的兼容性问题,因为所有的库代码都已经集成到了可执行文件中。
2.动态链接
概念
动态链接库(DLL在Windows或.so在Unix-like系统)是包含可以被多个程序共享的代码和数据的文件。这些文件在运行时被加载到内存中,供需要它们的程序使用。
过程
在动态链接过程中,链接器并不将库的代码合并到可执行文件中,而是存储库代码所在位置的引用。当程序启动时,动态链接器(运行时的一部分)会加载这些库到内存,如果多个程序使用同一库,它们可以共享内存中的同一份副本。
3.链接器的作用
- 符号解析: 链接器解析程序代码中对变量和函数的引用,将它们与正确的地址关联起来。
- 地址绑定: 链接器决定代码和数据在内存中的地址,并更新程序中的引用。
- 空间布局: 链接器负责安排代码和数据在可执行文件中的布局。
- 依赖处理: 对于动态链接,链接器还负责记录程序所依赖的动态库信息。
四、装入与链接的实战演练
1.环境设置
编译器
例如,如果是C语言,可以安装GCC(GNU Compiler Collection)。在Ubuntu系统上,使用以下命令安装GCC:
sudo apt update
sudo apt install build-essential
链接器
链接器通常与编译器捆绑在一起。GCC内置的链接器ld会自动被调用,无需单独安装。
2.编译与装入
编译
使用GCC编译程序:
gcc -o main main.c
装入
编译完成后,操作系统的装入器会在程序执行时将其装入内存。
3.链接实例
静态链接
假设有一个静态库libmath.a
,包含数学计算函数。编译时链接这个库:
gcc -static -o main main.c -L. -lmath
动态链接
如果libmath
有对应的动态版本libmath.so
,编译时使用动态链接:
gcc -o main main.c -L. -lmath
在运行时,动态链接器会查找并加载libmath.so
。