链接库
什么是库?
在计算机科学中,库(英语:Library)是用于开发软件的子程序集合。库和可执行文件的区别在于,库不是独立程序,它们是向其他程序提供服务的代码。简而言之,库是复用代码的集合,是一些最常用的代码编译成目标文件后打包存放。
那什么是链接库呢?顾名思义,链接库就是程序在链接时所需要使用的复用代码。
从Hello,world说起
现在我们写了一个Helloworld程序想要运行,但是我们必须得编译。
// Helloworld.c
#include <stdio.h>
int main()
{
printf("Hello,world");
}
我们知道,程序的编译分为四个步骤,并且每个步骤都有对应的工具:
- 预处理(Prepressing),使用预处理器。
- 编译(Compilation),使用编译器。
- 汇编(Assembly),使用汇编器as。
- 链接(Linking,这一步就要用到链接库啦) ,使用链接器ld。
每个步骤都干了啥呢?我结合许多网上讲解和书本知识给大家说一下。
1. 预处理
预处理过程主要处理那些源代码文件中以“#”开始的预处理指令。如上面程序中的#include、还有#define等。
主要处理规则如下:
- 将所有的“#define”删除,并且展开所有宏定义。
- 处理所有条件预处理指令,比如“#if”、“#ifndef”、“elif”等等。
- 处理“#include”指令,将被包含文件插入到对应的位置,这个过程是递归的,即被包含的文件可能还包含其他文件。
- 删除所有注释“//”和“/* */”。
- 添加行号和文件名标识,比如#2 helloworld.c,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时显示行号。
- 保留所有的#pragma编译器指令。这个我没用过不太懂。
在Linux下使用命令对程序进行预处理: gcc -E Helloworld.c -o Helloworld.i
-E 表示只进行预处理。
上图是部分展示,可以看到我们的头文件已经不见了,转换成了一系列的extern声明。
2. 编译
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件。
Linux下使用命令对预处理后的文件进行编译:gcc -S Helloworld.i -o Helloworld.s
-S 表示只进行编译不进行后面的汇编。
这部分过于复杂,且不是重点,我们知道这步可以得到汇编代码文件就可以了。
3. 汇编
汇编过程是将汇编代码转换成机器可以执行的指令,即二进制指令序列。
Linux下使用命令对汇编代码文件进行汇编:as Helloworld.s -o Helloworld.o
上图使用objdump解析二进制。左边是汇编后的二进制,右边对应的是汇编代码。
其实,这里得到的.o文件称为可重定位文件(Relocatable File),是ELF格式的第一种文件,这种文件还不能直接加载到内存中运行。
4. 链接
程序设计的模块化是人们一直在追求的目标,因为当一个系统十分复杂的时候,我们不得不将一个复杂的系统逐步分割成小的系统已达到各个突破的目的。一个复杂的软件也如此,人们把每个源代码独立的编译,然后按照须要将它们“组装”起来,这个组装模块的过程就是链接(Linking)。
链接后得到的才是真正的可执行文件(Executable file),这是ELF格式的第二种文件,可以直接加载到内存中运行。
上面的Helloworld程序之所以要链接是因为printf函数,虽然我们include了<stdio.h>,但这里面仅仅是函数的声明,函数的具体实现,存放在链接库里,需要链接才能完善这个代码。
静态链接库VS动态链接库
链接分两种:
- 静态链接:是指将要调用的函数链接到可执行文件中,成为可执行文件的一部分。
- 动态链接:相对静态链接而言,所调用的函数没有链接到可执行文件中,而是仅仅在其中加入了所调用函数的描述信息(一般是重定位信息)。仅当程序被装入内存运行时,才通过描述信息链接。
为什么会有动态链接呢?
- 静态链接容易浪费空间,如果有多个程序使用该静态库,内存中将存在多份静态库的拷贝。
- 静态库如果更新,那么使用静态库的程序都得重新编译。
静态链接库,就是静态链接时使用的库,linux下的.a,windows下的.lib;
动态链接库,就是动态链接时使用的库,linux下的.so,windows下的.dll;
动态链接库不会在程序编译时链接到目标代码中,而是程序运行才加载。不同的程序如果调用了相同的库,那么内存里只需要一份该共享库的实例,规避了空间浪费问题,因此动态链接库也称为共享库文件(shared object file),是ELF格式的第三种文件。并且,因为是运行时才加载,也解决了静态库对程序的更新问题,用户只需要更新动态库即可。