【Linux笔记】动态库与静态库的制作
🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:Linux
🌹往期回顾🌹:【Linux笔记】理解Ext2文件系统(下)
🔖流水不争,争的是滔滔不
- 一、什么是库
- 二、静态库库的制作
- 三、动态库的制作
- 目标文件
一、什么是库
库就像是编程世界里的 “工具包”,里面装着别人已经写好的代码模块,可以直接拿过来用。比如你要建一座房子,不需要自己炼铁造钉子,直接买现成的钉子(库)就能加快速度。
静态库(.a/.lib)
特点:编译时 “打包” 进你的程序,变成程序的一部分。
比喻:像外卖套餐里的固定配菜,直接塞进你的饭盒带走。
优点:独立运行,不需要额外依赖;
缺点:程序体积变大,修改库后需要重新编译整个程序。
动态库(.so/.dll)
特点:运行时才 “链接” 到程序,程序和库是分开的文件。
比喻:像外卖平台,每次点餐时实时调用配送服务。
优点:节省内存(多个程序可共享同一个库),更新库时不用改程序;
缺点:依赖外部文件,部署时可能需要额外安装。
库是代码复用的 “预制件”,让开发者不用重复造轮子。静态库像 “预制菜”,编译时直接嵌入;动态库像 “即点即做”,运行时调用。按需选择,静态库省心但占空间,动态库灵活但依赖环境。
二、静态库库的制作
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再
需要静态库。
⼀个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译器默认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使用 gcc 的 -static 强转设置链接静态库。
举个例子
我们写的项目,分为头文件和源文件以及自己的test也就是调用方法实现目的的文件,mystdio.h和mystring.h是头文件声明了方法,mystdio.c和mystring.c是原文件,usercode.c是测试文件。
我们通过编译形成.o文件,然后形成可执行。
那么问题来了,我要是把文件发给别人,直接给头文件和,o文件,别人直接用他的测试文件跟你的头文件和.o文件编译链接就可以形成可执行。
把.h文件和.o文件发给别人,他自己对源文件的方法进行调用写出自己的源文件。最后都编译为.o文件,链接形成可执行
也就是说,其他用户拿到你源文件编译好的.o文件和.h声明,就可和自己的源文件(进行方法调用)进行连接形成可执行。
如果有几千几百个.o文件发给别人未免太繁琐,所以我们可以借助 ar(archive)工具来创建静态库。
静态库本质上是把多个目标文件(.o 文件)打包在一起形成的一个文件。目标文件是源代码经过编译后生成的中间文件,而静态库则是对这些目标文件的一种集合形式。在编译程序时,静态库会被完整地复制到可执行文件中。
创建静态库
一般会借助 ar(archive)工具来创建静态库。以下是创建静态库的基本步骤:
- 编写源文件,例如 func1.c 和 func2.c。
gcc -c func1.c func2.c
- 使用 ar 工具将目标文件打包成静态库:
ar rcs libexample.a func1.o func2.o
- 这里,libexample.a 就是生成的静态库文件名,命名规则一般是 lib 加上库名,再加上 .a 后缀。
.a静态库本质是一种归档文件,不需要使用者解包,而是适应gcc/g++直接编译。
这里打包好的静态库,给其他人使用,另一个人应该如何使用静态库
- 头文件和库文件我自己的源文件在同一路径下
gcc -o usercode usercode.o -L. -lmyc
我们在我们的路径下建一个目录lib,lib下分别建include目录和mylib目录,分别存储头文件和静态库。这里包含头文件的声明和静态库文件,如果另一个用户要用的话他有自己的源文件对方法进行调用,这个lib目录里就都包含了。
现在把lib给另一个用户
- 头文件和库文件有自己独立的路径
gcc -o usercode usercode.c -I ./lib/include/ -L ./lib/mylib/ -l myc
- -L: 指定库路径
- -I(大i) : 指定头文件搜索路径
- -l (小L) : 指定库名
上述都不是把库安装到系统里,都是在用户自己的路径下
当我们把头文件和静态库文件都安装到系统的指定路径下
- 头文件和库文件安装到系统路径下
gcc usercode.c -lmyc
三、动态库的制作
动态库(.so):程序在运⾏的时候才去链接动态库的代码,多个程序共享使用库的代码。
⼀个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的⼀个表,而不是外部函数所在目标文件的整个机器码
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的⼀份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
动态库要想形成.o文件
gcc -fPIC -c *.c
fPIC:产生位置无关码(position independent code)
.o文件链接形成动态库
gcc -shared -o libmyc.so *,o
跟之前操作一样打个包发给另一个用户。
我们用之前的方式,头文件和库文件有自己独立的路径的方式进行编译,但是发现编译的时候过了但是运行的时候报错。
注意静态库编译完后,静态库在链接的时候直接把库的实现拷贝到了我们的可执行程序中,一但形成可执行程序,可执行程序就不在依赖静态库了。直接加载到内存就可以执行了。但是动态库在加载程序的同时也要找到所依赖的库。
静态库只有编译器gcc拿到了这个库,但是系统并不知道,所以编译过了但是运行不过
解决方式
-
可以直接把动态库拷贝进操作系统的指定路径下
-
向系统共享库路径下建立同名软连接
-
更改环境变量: LD_LIBRARY_PATH
但是存环境变量的方式是内存级的,关上再打开就没了。 -
ldconfig方案:配置/ etc/ld.so.conf.d/ ,ldconfig更新
在 Linux 系统里,/etc/ld.so.conf.d/ 目录的作用是存放动态链接器的配置文件。动态链接器(ld.so 或者
ld-linux.so)在运行时会查找共享库(.so 文件),而 /etc/ld.so.conf.d/目录下的配置文件能够对其搜索路径进行定义。
创建配置文件
在 /etc/ld.so.conf.d/ 目录下创建一个新的配置文件,文件名可以根据实际情况来定,一般以 .conf 作为扩展名。比如,创建一个名为 my_custom_libs.conf 的文件:
bash sudo touch /etc/ld.so.conf.d/my_custom_libs.conf
编辑配置文件 运用文本编辑器(像 nano 或者 vim)来打开刚创建的配置文件,然后添加动态库所在的目录路径。
bash sudo nano /etc/ld.so.conf.d/my_custom_libs.conf
在文件中添加如下内容:
bash /opt/my_custom_libs
如果你有多个目录需要添加,可以每行写一个目录,或者用空格分隔多个目录,示例如下:
bash /opt/my_custom_libs /usr/local/my_libs
更新动态链接器缓存 完成配置文件的编辑之后,要运行 ldconfig 命令来更新动态链接器的缓存。这样系统就能识别新添加的搜索路径了。
bash sudo ldconfig
ldconfig 命令会读取 /etc/ld.so.conf 文件以及 /etc/ld.so.conf.d/ 目录下的所有配置文件,然后更新 /etc/ld.so.cache
文件,这个文件包含了动态链接器能够找到的所有共享库的信息。
gcc/g++默认使用动态库
如果动态库和静态库同时存在,只想用静态链接的时候,就用-static。如果用了-static就必须有对应的静态库
在Linux系统下,默认情况安装的大部分库,默认都是安装的动态库
一个库可以有多个应用程序对其进行调用
目标文件
可以看到,在编译之后会生成两个扩展名为 .o 的文件,它们被称作目标⽂件。要注意的是如果我们修改了⼀个原文件,那么只需要单独编译它这⼀个,而不需要浪费时间重新编译整个⼯程。目标文件是⼀个⼆进制的文件,文件的格式是 ELF ,是对⼆进制代码的⼀种封装。
目标文件(.o) 是源代码经过编译器处理后生成的二进制文件,是程序从源代码到可执行文件的中间产物。它包含机器码(CPU 能识别的指令),但还未完全链接成可执行文件。