在 linux 中构建、链接进程

use*_*704 4 linux

我无法区分构建和编译。他们一样吗?链接究竟是如何工作的?.so 文件和 .o 文件到底包含什么以及我应该如何使用它们?这些我每天都会看到的所有文件,但我不知道它们到底包含什么。任何人都可以建议一些教程来清楚地说明这些过程吗?

Mat*_*Mat 12

术语“构建”通常用于表示从一组源代码文件和其他资源开始,到一组可执行文件、共享库(可能还有其他资源)结束的整个过程。
这可能涉及很多步骤,例如特殊的预处理器(moc例如 Qt 代码)、代码生成器(flex/yaccbison例如)、编译、链接以及可能的后处理步骤(例如构建tar.gzrpm分发文件)。

对于 C 和 C++(以及相关语言),编译是将源文件(例如.cC 代码文件)转换为目标文件 ( .o) 的过程。这些目标文件包含编译器为相应的源代码生成的机器代码,但不是最终产品——特别是,外部函数(和数据)引用没有被解析。从这个意义上说,它们是“不完整的”。
目标文件有时被组合成档案(.a文件),也称为静态库。这几乎只是将它们组合在一起的一种便捷方式。

链接需要(通常是几个)目标文件(.o.a)和共享库,组合目标文件,解析(主要)目标文件本身和共享库之间的引用,并生成您可以实际使用的可执行文件,或共享库(.so)可以被其他程序或共享库使用。

共享库是其他可执行文件可以直接使用的代码/函数的存储库。针对共享库的动态链接与(静态)直接链接对象或存档文件之间的主要区别在于,无需重新构建使用它们的可执行文件即可更新共享库(尽管对此有很多限制)。
例如,如果在某个时候在 OpenSSL 共享库中发现错误,则可以在该代码中进行修复,并且可以生成和交付更新的共享库。动态链接到该共享库的程序不需要重新构建以获得错误修复。更新共享库会自动修复其所有用户。
如果他们改为与目标文件链接(或通常静态链接),他们将不得不重建(或至少重新链接)以获得修复。

一个实际的例子:假设你想用 C 编写一个程序 - 一个花哨的命令行计算器 - 具有命令行历史/编辑支持。您将编写计算器代码,但您将使用readline库进行输入处理。
您可以将代码分成两部分:数学函数(将这些函数放在 中mathfuncs.c)和处理输入/输出的“主要”计算器代码(例如在 中main.c)。

您的构建将包括:

  • Compile mathfuncs.c ( gcc -o mathfuncs.o -c mathfuncs.c-c代表“仅编译”)
    mathfuncs.o现在包含您编译的数学函数,但不是“可执行的”——它只是函数代码的存储库。

  • 编译你的前端 ( gcc -o main.o -c main.c)
    main.o同样只是一堆函数,不可运行

  • 链接您的计算器可执行文件,链接readline

    gcc -o supercalc main.o mathfuncs.o -lreadline
    #      ^ executable                    ^ dynamic link with libreadline.so
    #                  ^         ^ two .o files statically linked in
    
    Run Code Online (Sandbox Code Playgroud)

    现在您有了一个可以运行的真正可执行文件 ( supercalc),这取决于readline库。

  • 构建一个rpm包含所有可执行文件和共享库(和头文件)的包。(这些.o文件是临时构建产品而不是最终产品,通常不会发货。)

有了这个,如果在 中发现错误readline,您将不必重建(和重新发送)您的可执行文件来获得修复 - 更新libreadline.so就是所需的全部。但是,如果您在 中发现错误mathfuncs.c,则需要重新编译并重新链接supercalc(并发布新版本)。