Linux:动静态库
主函数测试代码库里面不应该有main函数。
目录
总结:
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
主函数测试代码
#include "Add.h" #include "Sub.h"
#include "Mul.h"
#include "Div.h"
int main()
{
int x = 20;
int y = 10;
printf("%d + %d = %d\n",x,y,Add(x,y));
printf("%d - %d = %d\n",x,y,Sub(x,y));
printf("%d * %d = %d\n",x,y,Mul(x,y));
return 0;
}
库里面不应该有main函数
1.静态库
程序中包含的头文件会在当前路径和默认路径下找。不使用库文件生成可执行程序的方式:
gcc -o test Add.c Div.c Mul.c Sub.c testmain.c //直接使用.c文件生成可执行程序
gcc -c Sub.c 默认生成同名.o 不用 -o Sub.o
1.1 生成静态库

静态库需要使用ar命令把 .o 文件打包到一起,打包成以 lib 开头以 .a 结束的库文件。本质就是将库中的源代码直接翻译成为 .o 目标二进制文件,然后打包,ar是gun归档工具,rc表示(replace and create)。
1.2 使用静态库
gcc 第三方库默认不认识, 当前路径也不行,只使用c标准库
gcc Testmain.c -l math -L . // -l ,link 链接指定库 ,去掉lib .a -L lib指定库的路径
ldd a.out 可以查看一个可执行程序依赖的动态库文件,不显示静态库是因为静态库已经拷贝到可执行程序内了。
ldd a.out
gcc -static 表示全部使用静态库,必须要 .a,这里mymath是静态库,所以使用静态链接 。gcc能动态链接就会动态链接,默认动态链接,如果库没有动态库,会使用静态链接。
1.3 生成第三方库

对 mymath_lib 目录压缩就可以被别人下载使用。
//压缩
tar -czf mymath_lib.tgz mymath_lib
//解压
tar xzf mymath_lib.tgz
由于库中的头文件不在当前目录和默认路径/usr/include下,所以生成可执行文件时gcc要加 -I Include指定头文件位置,-l link指定使用的库名称,-L Lib说明库所在的位置,在默认路径 lib64/ 可以不指明。

gcc TestMain.c -I mymath_lib/include #-I include,说用头文件位置
gcc TestMain.c -I mymath_lib/include -l mymath -L mymath_lib/lib
如何减少使用 gcc 所带的选项:
- 对于需要指定头文件目录,可以使用 .c文件中使用 #include "相对路径" 解决。
- /usr/include 默认头文件目录。lib64/ 默认库文件目录。三方库.h可以直接安装到/usr/include ,库安装到lib64/ 。不用带 -Include -L,但要带-link,因为要指明使用的库,c语言标准库默认不用link指明
通过静态库生成的可执行程序, 可以执行程序与静态库被打包在一起,可以直接运行,运行期间不需要查找静态库。.h文件相当于库文件的使用说明书。
2.动态库
也是生成 .o 文件打包, 使用gcc -fPIC 产生位置无关码。动态库使用 gcc-shared .o打包 。形成动态库的方法直接内置到gcc中去了,而静态库没有,说明动态库比较重要
2.1 形成动态库
使用 gcc 时,要加上 fPIC 选项,生成位置无关码的 .o 文件,关于位置无关码我们后面讲。

生成可执行程序:
gcc testmain.c -I mymath_dylib/include -l mymath -L mymath_dylib/lib
可执行程序与静态库是打包在一起的,一旦编译成功,也就是把静态库拷贝到可执行程序中了,所以运行时不再需要使用静态库。而动态库与可执行程序是分离的,是两个文件,运行时可执行程序被加载到内存,动态库也需要被系统找到。

为什么已经告诉 gcc 编译器 动态库的地址还不能运行
因为运行时与编译器没有关系,和系统有关系,我们只是告诉编译器库的位置。应该让系统知道动态库在哪里,系统只去默认目录lib64/下去找,而我们自己的动态库不在那里,
动态库与静态库的形成与使用没有太大差别,动态库形成时使用gcc特定选项形成,静态库使用ar打包,生成可以执行程序时动静态库gcc选项相同。
差别是:使用动态库的可执行程序加载时,不能直接执行,需要将动态库也加载上。
2.2 如何执行程序时使用动态库
方法 一: 直接将动态库 安装到系统默认路径下
![]()
![]()
![]()
系统中有很多库,指明链接自己的库,c标准库不用。由于头文件和库文件都被拷贝到默认路径下,gcc 编译时不用说明,且执行时可以直接执行。
方法二:将库所在目录添加到环境变量 LD_LIBRARY_PATH
可执行程序在运行时,系统会帮我们去找动态库,系统会 lib64/ 中寻找,还会在环境变量LD_LIBRARY_PATH中寻找。动态库可以是库本身,也可以是库的软链接。
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:./mymath_dylib/lib
下次重新登录时,该环境变量会重新读取系统配置文件,而配置文件中没有,该变量只是内存级别,可以通过改配置文件改变。ldd a.out 可以查看到链接的动态库
方法三:直接更改系统关于系统关于动态库的配置文件
ls /etc/ld.so.conf.d/
ld:load。so:动态库。conf:配置。d是目录
在该目录下创建文件,vim写入该库所在的绝对路径,然后刷新使用ldconfig命令即可。
如果使用别人的第三方库,动态库和静态库都有提供:

gcc TestMain.c -I mymath_dylib/include/ -l mymath -L mymath_dylib/lib
没有指明用动态库还是静态库时,系统默认优先使用动 态库,但是还是要使用上面的三种方法之一。使用静态库要加-static,但是这时使用的库全是静态库
gcc TestMain.c -I mymath_dylib/include/ -l mymath -L mymath_dylib/lib -static
yum下载库的原理:
sudo yum install -y ncurses-devel //下载库
ls /usr/include/ncurses.h //查找是否存在
ls /lib64/libncurses.so
gcc test.c -l ncurses //使用库
用别人的库下载安装,即把头文件和库文件拷贝到系统的指定路径下。
3.动态库的加载
3.1 可程序程序和动态库
静态库编译时已经拷贝到程序中,不考虑加载
gcc -fPIC -c xxx.c 产生位置无关码。Linux链接这些 .o 后生成ELF格式的2进制可执行程序。
可执行程序包含符号表,记录使用的函数在哪个库中,记录该方法在库中的地址。因为程序执行时这里没有具体库函数的实现,只有方法的在库中的地址,所以运行时需要将使用的库也加载到内存。 所以动态链接的程序,不光光自己可执行程序要加载到内存,链接的库也要加载到内存。
3.2 可执行程序内的库函数
可执行程序没有被加载,程序中函数名的概念,会变成对应的虚拟(逻辑 )地址,基地址+偏移量的形式,但最终执行的时候都会通过页表变成真实地址。基地址为0称为平坦模式,编译时会对代码进行编址,基本遵守虚拟地址空间的那一套。
虚拟地址空间不仅仅时操作系统的概念。编译器编译的时候,也要按照这样的规则编译可执行程序,这样才能在加载的时候,进行从磁盘文件到内存,再进行映射。
3.3 绝对编址与相对编址
绝对编址适合可执行程序为平坦模式,相对编址适合库中函数地址,记录函数在库中的地址,未来与库在内存中的起始地址变化,库内地址是不变的,所以叫做与位置无关码。
3.4 虚拟地址的共享区
静态库内函数被拷贝到可执行程序中了,形成.o链接到一起。运行时与静态库无关,不存在加载静态库问题。
动态库在使用时需要加载到内存中,要通过页表映射到使用该库的进程的虚拟地址空间中的共享区,内存中还是只有一份,虚拟地址空间在 task_struct 中被描述起来,比如起始结束地址。但是并不能保证将库映射到共享区的固定位置,一旦加载完成 就固定位置了,确定起始地址, 他们都可以正常运行。之前程序运行函数调用实在正文代码中跳转,现在可以跳转到共享区的库函数中,使用完再返回。

3.5 地址无关码
可执行程序在链接动态库时,只会保存用到的库名称以及函数在库中的地址。使用该函数时通过库的起始地址和位置无关码使用。位置无关码就是函数在库中的相对地址。 调用方式与调用普通函数可以说是相同的。
一个库可能被多个进程使用,但内存中只有一份,通过不同进程的页表来映射到自己的共享区,所以称作共享库(动态库)。
静态库会拷贝到不同的可执行程序中,存在于正文代码区域,内存中可能存在多份,会浪费内存空间。
程序执行过程:程序的代码加载到内存,也要占据物理内存。可执行程序会在自己特定的位置记录下来自己程序的入口地址entry,也是虚拟地址,供操作系统读取,放入cpu的指令寄存器开始运行,指令寄存器也是使用的虚拟地址,通过页表转换为物理地址。可执行程序内部都是使用虚拟地址,使用虚拟地址空间。
本篇结束!
更多推荐



所有评论(0)