当前位置: 首页 > news >正文

计算机操作系统 — 链接

img

😀前言
这篇文章希望带你从零开始,一步步理解编译系统的工作流程,深入掌握静态链接与动态链接的原理,让你不再只是“会用 gcc”,而是真正搞懂程序是如何被构建、加载、运行的。

🏠个人主页:尘觉主页

文章目录

  • 计算机操作系统 — 链接
    • 一、写在前面:那一行 `gcc hello.c` 背后的故事
    • 二、从源代码到可执行文件的“旅程图”
    • 三、阶段一:预处理(Preprocessing)
    • 四、阶段二:编译(Compilation)
    • 五、阶段三:汇编(Assembling)
    • 六、阶段四:链接(Linking)
    • 七、三种目标文件类型
    • 八、静态链接 vs 动态链接
      • 1️⃣ 静态链接(Static Linking)
      • 2️⃣ 动态链接(Dynamic Linking)
    • 九、运行时加载(Loader 的工作)
    • 十、动手实验:探索编译的“黑箱”
    • 十一、总结:从源码到执行的一条生命线
    • 十二、延伸阅读

计算机操作系统 — 链接

一、写在前面:那一行 gcc hello.c 背后的故事

每个学编程的人几乎都从一句话开始:

#include <stdio.h>
int main() {printf("hello, world\n");return 0;
}

然后我们在命令行里输入:

gcc -o hello hello.c
./hello

屏幕输出:

hello, world

故事到这里似乎结束了,但对于操作系统来说,它才刚刚开始。
你有没有想过:这一行 gcc 命令,到底帮我们做了什么?
为什么写了 C 代码,最终却能变成 CPU 能执行的机器指令?


二、从源代码到可执行文件的“旅程图”

整个过程其实分为四个主要阶段:

预处理 → 编译 → 汇编 → 链接

可以理解为一个“流水线”:

hello.c  ──预处理──>  hello.i  ──编译──>  hello.s  ──汇编──>  hello.o  ──链接──>  hello

每一步都在让代码逐渐靠近“机器能懂”的世界。


三、阶段一:预处理(Preprocessing)

预处理就是在编译前“打扫战场”。

它主要做三件事:

  1. 展开 #include(把头文件内容插进来);
  2. 展开 #define 宏;
  3. 删除注释、处理条件编译。

命令行可以单独查看预处理结果:

gcc -E hello.c -o hello.i

打开 hello.i,你会看到 #include <stdio.h> 被替换成一大堆标准库声明。
这一步结束后,源文件变成了纯净的 C 代码,没有宏、没有注释。


四、阶段二:编译(Compilation)

编译器真正的主角登场了。

它把人类可读的 C 语言翻译成 汇编语言(Assembly)。
汇编是“贴近机器但仍然能看懂”的语言。

执行:

gcc -S hello.i -o hello.s

此时的 hello.s 内容大概长这样:

    .file   "hello.c".section    .rodata
.LC0:.string "hello, world".text.globl  main
main:pushq   %rbpmovq    %rsp, %rbpleaq    .LC0(%rip), %rdicall    printfmovl    $0, %eaxpopq    %rbpret

可以看到,它已经开始接近机器操作了。


五、阶段三:汇编(Assembling)

汇编器负责把 .s 文件变成机器能执行的二进制指令。
这一阶段输出的是 .o 文件(object file,也叫“目标文件”)。

gcc -c hello.s -o hello.o

.o 文件里包含机器码和符号信息,但还不能运行,因为它还不知道 printf 在哪。
此时的代码里到处是“占位符”。


六、阶段四:链接(Linking)

链接器就像一位“总导演”,它要把所有“零散的片段”组装成完整的电影。

主要干两件事:

阶段说明
符号解析(Symbol Resolution)找到每个函数或变量真正的定义,比如 printf 在哪里实现
重定位(Relocation)把所有符号引用替换成它们在内存中的真实地址

执行:

gcc -o hello hello.o

链接器会自动去找 libc 标准库,把 printf 解析到正确的函数地址中。
最终生成的 hello 文件就是可执行程序。


七、三种目标文件类型

在整个编译过程中,我们会遇到三种不同类型的目标文件:

类型描述示例
可重定位目标文件 (.o)中间产物,包含机器码和符号信息,不能直接运行hello.o
可执行目标文件链接完成后生成,可直接运行hello
共享目标文件 (.so)特殊的可重定位文件,可在运行时被多个程序共享加载libc.so.6

八、静态链接 vs 动态链接

到这里,我们就得谈谈两种最重要的链接方式。

1️⃣ 静态链接(Static Linking)

静态链接器会把所有库的实现代码都复制进可执行文件中。

gcc -static -o hello_static hello.c

🔹 优点:

  • 程序完全独立,不依赖外部库文件;
  • 部署方便。

🔹 缺点:

  • 可执行文件体积大;
  • 库更新后需要重新编译;
  • 内存中无法共享同一个库的副本。

2️⃣ 动态链接(Dynamic Linking)

动态链接不会把库代码复制进程序,而是让程序在运行时去共享加载

例如:

gcc -o hello hello.c
ldd hello

输出:

linux-vdso.so.1 =>  (0x00007ffd26dff000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff51b700000)

表示程序运行时会加载系统里的 libc.so.6

🔹 优点:

  • 节省磁盘和内存(多个进程共享同一库);
  • 库升级无需重新编译。

🔹 缺点:

  • 程序依赖外部库文件;
  • 库版本不兼容可能导致运行错误。


九、运行时加载(Loader 的工作)

当你输入 ./hello 时,发生的事比你想象的多:

  1. 操作系统把可执行文件加载进内存;
  2. 动态链接器找到所需的 .so 库;
  3. 完成符号解析(绑定函数地址);
  4. 跳转到 main(),开始执行。

动态链接还有两种策略:

类型说明
立即绑定(Eager Binding)程序启动时就加载所有库函数地址
延迟绑定(Lazy Binding)只在第一次调用函数时才加载,提高启动速度

十、动手实验:探索编译的“黑箱”

命令作用
gcc -E hello.c -o hello.i只做预处理
gcc -S hello.c -o hello.s生成汇编代码
gcc -c hello.c -o hello.o生成目标文件
nm hello.o查看符号表
ldd hello查看动态库依赖
objdump -d hello反汇编可执行文件
readelf -h hello查看 ELF 文件头

👉 小练习:

gcc -o hello hello.c
gcc -static -o hello_static hello.c
ls -lh
ldd hello
ldd hello_static

对比文件大小和依赖库的不同,你会更直观地理解静态与动态链接的差异。


十一、总结:从源码到执行的一条生命线

阶段输入输出主要任务
预处理hello.chello.i展开宏与头文件
编译hello.ihello.s生成汇编
汇编hello.shello.o转成机器码
链接hello.ohello符号解析与重定位

🧭 一句话记忆:

链接 = 符号解析 + 重定位
静态 = 打包所有库
动态 = 运行时加载共享库


十二、延伸阅读

  • 《深入理解计算机系统》(CS:APP)第七章

  • Linux 动态链接器 /lib64/ld-linux-x86-64.so.2

  • LD_PRELOAD 与延迟绑定机制

  • 自定义共享库实践:

    gcc -fPIC -shared -o libmylib.so mylib.c
    gcc -o test test.c -L. -lmylib
    ldd ./test
    

😁热门专栏推荐
想学习vue的可以看看这个

java基础合集

数据库合集

redis合集

nginx合集

linux合集

手写机制

微服务组件

spring_尘觉

springMVC

mybits

等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

🤔欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😁
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

img

http://www.dtcms.com/a/531780.html

相关文章:

  • 网站图片加altwordpress前端库加速
  • 在linux上使用docker搭建ELK日志框架
  • Docker 应该如何学习 分四个阶段
  • 面试过程中的扣分项,你踩过几个?
  • 中牟高端网站建设专做户外装备测评视频网站
  • CSS属性(二)
  • 2011年下半年试题四:论软件需求获取技术及应用
  • Mujoco 仿真 PPO 强化学习机械臂末端路径规划到达指定位置(代码讲解)
  • 【C#】EventHandler的使用
  • C++ 实际应用系列(第六部分):并发系统的性能优化与工程实践(完)
  • 上市公司网站建设分析wordpress 转 app
  • Prometheus+Grafana 智能监控告警系统(服务器指标采集、mysql指标采集)
  • html5电影网站如何做企业网站流量怎么做
  • <数据集>yolo煤矿安全帽识别数据集<目标检测>
  • excel中加载数据分析工具的步骤
  • 一文厘清:文库 vs 知识库、核心功能清单、开源方案对比
  • 图片转excel vlm 提取手写清单信息 Qwen/Qwen3-VL-235B-A22B-Instruct
  • webrtc代码走读(七)-QOS-FEC-ulpfec rfc5109
  • 第十五章认识Ajax(六)
  • 逻辑回归解释
  • B038基于博途西门子1200PLC物料分拣控制系统仿真
  • 第十二章认识Ajax(三)
  • Spring Boot3零基础教程,安装 docker,笔记67
  • FLOW翻译
  • 第十六章jQuery中的Ajax
  • 实现 AI 流式响应:从等待到实时交互的技术解析
  • 东莞站福公司工资室内设计手绘图 基础入门
  • HTTPS 加密原理介绍
  • 小白python入门 - 9. Python 列表2 ——从基础操作到高级应用
  • 日本生活-东京新干线乘车经验-流程介绍