链接 静态库和动态库的区别 《深入理解计算机系统 第七章》

[toc]

概述

静态库、动态库的本质区别。关键在于 编译和链接

不搞清楚这俩东西,就只能知道静态库是运行前加载的,动态库是运行时或者运行后加载的,不知所以然。

带星号的标题最好看下


当然细节也不用太过深究,只要知道大概这些模块都干了啥就行了。
所有图都取自于 CMU CSAPP 的PPT。
https://www.bilibili.com/video/BV1iW411d7hd?p=13

编译、链接、运行过程

下面两个.c 是怎么最终成为可执行程序并被运行的?


image.png




  • 第一步:
    编译器分别编译两个.c,这里的编译器包括预处理器、C编译器、汇编器,最终生成 main.o sum.o,叫做可重定位目标文件 (relocatable object file)
    .o 是怎么从 .c来的这里不扩展。详情看 CSAPP 7.1
  • 第二步:
    链接器被调用,把main.o 和 sum.o 以及一些必要的系统目标文件组合起来,创建一个可执行目标文件 prog.out

  • 第三步:
    加载器被调用,把刚刚生成的可执行文件 prog.out 的 code 和 data 复制到内存,就可以运行了。

image.png



链接器作用 可重定位目标文件 xxx.o 是什么

简单理解:.o 继承了 .c 想做的所有功能,并且携带符号位置信息,可以帮助链接器最后把这些 .o 整合起来,做成一个可执行程序。

换句话说,静态库、动态库,其实就是这些.o 的集合体

把你的.c 编成 .o 后,它里面保存了所有的 全局变量的结构、text代码、data数据、bss数据、对外暴露的符号表(所有不加static的全局变量和函数) 、debug信息、重定位信息

image.png

符号表的作用是:编译器告诉链接器,你在链接的时候,一定要记得把当前的这些代码和数据,给确定好位置,因为汇编器自己不知道,在程序运行时,这个符号会储存在内存的哪里。

重定位是个非常难懂的过程,我汇编已经全忘了,详情请自己看书。

链接符号 *

  • 全局符号
    对于当前的模块A,内部定义的,不是static标记的 全局变量 或者 函数名称

  • 外部符号
    对于当前模块A,不属于内部定义的,其他模块定义的全局符号

  • 局部符号
    对于当前模块A,被static标记的 全局变量 或者 函数名称。由于加了static,所以这些东西不会被暴露给其他文件。

  • 局部符号不是局部变量
    因为局部变量是程序在运行时生成在栈里面的,链接器感知不到。而我们上面提到的所有东西,都是放在 data 和 bss 里面的。
    并且要记住,static变量即使是在一个花括号的函数中,被定义的,它也会保存在 data 或者 bss里面,而不是栈里面。


同名全局变量引发的 “强弱符号” 巨坑

在我们的代码仓中,一般禁止出现同名全局变量,所以这个问题不会出现。但是有必要了解它的原理。

链接器如何解决同名符号?

  • 强符号:
    被赋值初值的全局变量或者函数,是强符号

  • 弱符号:
    没有被赋值初值的全局变量或者函数,是弱符号

  • 符号解析规则
    1、同名符号间,强符号唯一,否则报错
    2、同时有强弱符号,选择强符号
    3、只有多个弱符号,随机选择一个

链接器处理强弱符号时,可能发生的问题 *

  • 两个不同模块都定义了 x 全局变量。一个赋值了,一个没有。那么没赋值的那个模块会使用另一个x,很难定位。


    image.png
  • 更恶心的是如果类型都不同,那么可能你只写了int,最后别人用double去取数值,越界。


    image.png


静态库 static library

静态、动态库就是 .o文件的集合体

为什么要建立静态库

写了个hello world.c,运行的时候得用 C 标准函数吧,如果没有这个库,怎么调用人家的函数?

简单粗暴点,直接把 C 标准函数都给整到一个 亿行代码的.c,然后出 .o ,最后把你的 .o 和人家的一块链接起来,就能跑了。

好像也能跑,但是一个C标准函数编完了,最后大小是5MB,你的每一个进程,都得带着 C标准.o 编译吧,这样当进程多起来的时候,就造成了内存的极大浪费

如果能做一个库,然后把 C标准的几百的 .c 都分别编成 几百个 .o 做成一个库。这个库里.o们,可以允许你自取所需,不要的直接扔掉,那效率就大大提升了。


静态库的 自取所需 是怎么实现的? *

链接器帮助实现,链接器在整合文件的时候。其实就是:

正常遍历你输入的所有 .o,CMakefile里面输入的那些 .c ,都会被按照顺序遍历


链接器会用一个 待办事项集合U,来保存所有的未被解析的符号。
目标文件集合E 来保存将会被整合为 可执行文件的 .o
已办完事项集合D 来保存所有的已经被解析的符号。

每当一个文件被遍历,首先判断,他是你想要编译的目标文件 .o,还是库文件 .a

如果是目标文件 .o,就把它放到E里面,然后更新 U 和 D。

如果是 库文件 .a,就尝试去匹配,看看这个文件里面有没有U里面缺的符号。

如果有,就把这些 待办事项 清理掉。同时把这个 .a中的 .o 扔到E里面。更新 U D
如果没有,说明这个 .o 是个废物,直接丢了就完事了。


如果遍历结束后,待办事项集合不为空,那么就报错


用下图来举例,如果我把 add.c 和 mult.c 做成了一个 libvector.a,但是 main.c 却没有调用 mult功能,那么链接器根本不会鸟 mult.o,会直接丢弃。

image.png
image.png

就像这样,mult.o 根本不会被取出来

image.png


自建一个库 由于链接顺序 产生的问题 *

链接顺序有问题,会导致链接错误。

举个最简单的例子:

如果你想链接 helloworld.o 和 printf.o ,如果链接器先遍历到 printf.o ,会直接把这玩意丢了,因为它的待办事项里面没有 printf

后面遍历到 helloworld.o 时,它会把 printf 加入到待办事项里面,但是printf.o已经没得了,待办事项 不为空,连接错误。

所以在CMakefile里面排顺序的时候要注意,否则就会出现明明都能找到定义,但是链接错误的问题。


动态库 shared library

为什么要用动态库? 静态、动态比较 *

  • 静态库还是不够省内存
    在使用静态库的时候,假设我们有上百个进程都用了 printf,那么 printf.o 就会被链接进这上百个进程,也是浪费。
    而动态库能做到,让这些进程,全都共享使用一个 printf.o 。怎么做到的我不知道。但是就是能做到。

  • 静态库更改,就得重编整个可执行文件
    静态库链接的时候,最基本的就是整合 .o 嘛,最后出一个可执行文件。
    如果你静态库改了,那么就得从新整个走一遍,再把它链接起来。

  • 一定程度的解耦?
    好多仓,之间有编译依赖顺序。其中 A仓 依赖 B仓,而当前 B仓给的对接件是 libB.a
    问题来了:当你的B仓改变的时候(涉及到了A仓调用的东西),你想让A仓生效,那就要去先编译 B,再去带着 B 的东西去编译 A,就麻烦了一点。如果把 B改成出 .so,那就搞好 so 被加载的顺序就成了。

  • 静态库的优点是加载快
    一般选用静态库,都是为了提高性能。

动态库可以在程序运行的 load-time 或者 run-time 去链接这个库


怎么做到的呢?
先用 .o集合 去做出一个 .so (使用 gcc 的 -shared),在第一次链接,出可执行程序这个阶段,需要把这个so里面符号表给导进去,但是不导内容。

这样子这个可执行程序就知道,将来我 起始或者运行 的时候,这个符号会被某个so给定义,我先不用管。

最终再动态的被调用去load 各个so。

不知道为啥就没办法截成一张图

image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容