链接

参考

CYC CS-Notes 计算机操作系统 - 链接

编译系统

以下是一个 hello.c 程序:

#include <stdio.h>

int main()
{
    printf("hello, world\n");
    return 0;
}

在 Unix 系统上,由编译器把源文件转换为目标文件。

gcc -o hello hello.c

这个过程大致如下:

  • 预处理阶段:处理以 # 开头的预处理命令。

  • 编译阶段:翻译成汇编文件。

  • 汇编阶段:将汇编文件翻译成可重定位目标文件。

  • 链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。

静态链接

静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务:

  • 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。

  • 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。

目标文件

  • 可执行目标文件:可以直接在内存中执行。

  • 可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件。

  • 共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接。

动态链接

静态库有以下两个问题:

  • 当静态库更新时那么整个程序都要重新进行链接。

  • 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。

共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点:

  • 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中;

  • 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。

Java 从源代码到运行的全过程

Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows, Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

什么是字节码?采用字节码的好处是什么?

在 Java 中,JVM 可以理解的代码就叫做字节码 (即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比较高效, 而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。

Java 程序从源代码到运行一般有下面 3 步:

需要格外注意的是 .class -> 机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器, JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,它会将字节码对应的机器码保存下来,下次可以直接使用。而机器码的运行效率是高于 Java 解释器的。这也解释了为什么经常会说 Java 是编译与解释共存的语言。

最后更新于