目录
十一、动态库和静态库
11.1 动态库和静态库定义
静态库定义:
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库
在 Linux 其后缀名一般为 “.a”,在Windows下其后缀一般为 “.lib”
动态库定义:
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
在 Linux下动态库一般后缀名为“.so”,在Windows下其后缀一般为 “.dll”
ldd 命令查看可执行程序所依赖的库
ldd 可执行程序名字
这里可执行程序所依赖的其中一个库是 libc.so.6,它实际上就是 C动态库,当我们去掉一个动静态库的前缀 lib,再去掉后缀 .so 及其后面的版本号,剩下的就是这个库的名字,比如 lib.so.6 去掉前缀和后缀后就剩下 c ,c 就是这个动态库的名字
file 命令则辨别可执行程序的类型,是静态库还是动态库
file 可执行文件名字
注:gcc/g++ 在编译时默认使用动态库进行链接
若想进行静态链接,可以携带一个 -static 选项
gcc -o mytest-s test.c -static //此选项对生成的文件采用静态链接
静态链接生成的可执行程序并不依赖其他库文件,此时当我们使用 ldd 命令查看该可执行程序所依赖的库文件时就会看到以下信息
明显发现静态链接生成的可执行程序的文件大小,比动态链接生成的可执行程序的文件大小要大得多
注意:使用 -static 首先要安装静态库,因为Linux下没自带有,输入如下指令进行安装,输入用户密码即可进行安装
//C语言静态库安装sudoyuminstall-yglibc-static//C++静态库安装sudoyuminstall-ylibstdc++-static
11.2 动静态库的基本原理
动静态库的本质是一堆目标文件(xxx.o)的集合
一个源程序经过以下步骤才会变成可执行程序:
- 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成 xxx.i 文件
- 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成 xxx.s 文件
- 汇编: 将汇编指令转换成二进制指令,最终形成 xxx.o 文件
- 链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序
动静库就是多个源文件经过预处理、编译、汇编,然后形成多个 .o 文件,然后再将这些 .o 文件链接起来,最终形成一个可执行程序
所有库本质都是一堆目标文件(xxx.o)的集合,库的文件当中并不包含主函数(main函数),而只是包含了大量的方法以供其他程序调用
11.3 静态库的打包与使用
11.3.1 静态库的打包
测试使用代码
add.h
externintadd(intx, inty);
add.c
intadd(intx, inty) { returnx+y; }
sub.h
externintsub(intx, inty);
sub.c
intsub(intx, inty) { returnx-y; }
目的:使用 add.c 和 sub.c 生成一个库
先介绍相关命令
gcc –c 源文件 –o 生成目标文件
“-c”选项把代码转换成二进制代码,即汇编完成就停下来,“-o”是指生成的目标文件
ar -rc 生成库的名字 源文件...
ar 命令是 gnu 的归档工具,常用于将目标文件打包为静态库,ar(archive)
- -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件
- -c(create):建立静态库文件
(1)将源文件编译生成 .o 文件
(2)使用 ar 命令生成静态库
(3)将头文件和生成的静态库组织起来,打包到同一个文件,再压缩就可以发给别人,别人就使用了
这样一个静态库就打包好了,但是使用一条条命令敲太麻烦了,可以使用 Makefile
libmymath.a:myadd.omysub.oar-rc$@$^myadd.o:add.cgcc-cadd.c-omyadd.omysub.o:sub.cgcc-csub.c-omysub.o.PHONY:cleanclean: rm-rf*.olibmymath.amylib.PHONY:outputoutput: mkdir-pmylib/includemkdir-pmylib/libcp-f*.amylib/libcp-f*.hmylib/include
注:output 就是把相关文件打包在一起
再 output 打包,进行压缩就可以发给别人了
11.3.2 静态库的使用
比如,张三想要使用这个库,你把库打包给了张三,张三解压到了自己的目录下,然后张三尝试使用你提供的库
测试代码:
该目录下只有一个测试的源文件和你提供的库
使用gcc编译main.c生成可执行程序时需要携带三个选项
- -I:指定头文件搜索路径(大写的i)
- -L:指定库文件搜索路径
- -l:指明需要链接库文件路径下的哪一个库(L的小写)
不指明这三个选项是编译不了的
gcctest.c-omytest-I./mylib/include-L./mylib/lib-lmymath
注:
- 因为编译器不知道你所包含的头文件add.h,sub.h在哪里,所以需要指定头文件的搜索路径
- 头文件 add.h,sub.h 当中只有 add(),sub() 函数的声明,并没有该函数的定义,所以还需要指定所要链接库文件的搜索路径
- 我们需要指明需要链接库文件路径下的哪一个库,因为 lib 目录下可能会有大量的库文件,编译器不知道使用哪一个库
- 库文件名去掉前缀 lib,再去掉后缀 .so 或 .a 及其后面的版本号,剩下的就是库的名字
- -I,-L,-l 选项后面可带空格也可以不带
- 我们平时使用 C/C++ 的头文件不用加这么繁琐的选项可以直接编译,这是因为我们所写的库不在系统的默认路径下
- 还要 -l(L的小写)指定库名字是因为要链接的是哪一个库编译器是不知道的,而我们使用的 C/C++ 头文件编译器默找的就是 C/C++ 的库,所以我们自己或他人写的库要指定库的名字
file 查看一下
怎么这么奇怪,我提供的不是静态库码,怎么还是动态链接?
- gcc 默认是动态链接的(建议行为),对于特定的一个库,究竟是动态链接还是静态链接,取决于你提供的是动态库还是静态库
- 一个可执行性程序不可能只依赖一个库函数,也就是说必须依赖多个库函数
- 但是,假设动静态库同时给你,编译器只能把静态库拷贝到可执行程序里面,然后只能进行动态链接,哪怕只有一个动态库剩下的全是静态库,链接依旧是动态链接
- 全部提供的是动态库,链接也是动态链接;提供有指定的静态库,再以静态的方式编译,这样链接就是静态链接
我们可以试着把库拷贝到系统路径下
进行拷贝
编译,运行
注:需要指明需要链接库文件路径下的哪一个库,因为编译的时候默认就找的是C库(使用的是 C语言)
实际上我们拷贝头文件和库文件到系统路径下的过程,就是安装的过程
记得把自己拷贝到系统路径下文件删去 !!
11.4 动态库的打包与使用
11.4.1 动态库的打包
代码依旧是上面的
(1)将源文件编译生成 .o 文件,这里要给 gcc 加多一个选项
-fPIC(position independent code):产生位置无关码
(2)生成动态库时我们不必使用ar命令,使用 gcc 生成动态库即可,这里也是多加了一个选项 -shared
gcc -shared -o libmymath.so sub.o add.o
(3)将头文件和生成的动态库组织起来,打包到同一个文件,再压缩就可以发给别人,别人就使用了
这样一个动态库就打包好了,但是使用一条条命令敲太麻烦了,可以使用 Makefile
直接 make 就可以生成一个动态库了
再 make output 打包,进行压缩就可以发给别人进行使用了
11.4.2 动态库的使用
比如,张三想要使用这个库,你把库打包给了张三,张三解压到了自己的目录下,然后张三尝试使用你提供的库,测试代码依旧是上面的
该目录下只有一个测试的源文件和你提供的库
使用该动态库的方法与刚才我们使用静态库的方法一样,我们既可以使用 -I,-L,-l 这三个选项来生成可执行程序,也可以先将头文件和库文件拷贝到系统目录下,然后仅使用 -l(L的小写)选项指明需要链接的库名字来生成可执行程序
- -I:指定头文件搜索路径(大写的i)
- -L:指定库文件搜索路径
- -l:指明需要链接库文件路径下的哪一个库(L的小写)
gcc test.c -o mytest -I./mylib/include -L./mylib/lib -lmymath
但是这次程序运行不了,可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库
ldd 查看
解决该问题的方法有以下三个:
(1)第一个是将库函数和相关的头文件拷贝到系统路径下,一般拷贝到这两个目录下,这个就不测试了
(2)更改添加 LD_LIBRARY_PATH
动态库加载会在系统路径下查找,也会在 LD_LIBRARY_PATH 这个环境变量下查找,添加这个动态库的路径到这个环境变量里面
exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/fy/code/code_linux/code_202301_12/d7/mylib/lib
再次运行
注:export 添加这个动态库的路径到这个环境变量里面,只会在当前登录的 shell 有效,下次登录无效,程序依旧无法运行
(3)配置 /ect/ld.so.conf.d/文件
可以通过配置 /etc/ld.so.conf.d/ 的方式解决该问题,/etc/ld.so.conf.d/ 路径下存放的全部都是以 .conf 为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在 /etc/ld.so.conf.d/ 路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库
若是将自己库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能够找到这个库文件了
在这个路径下新建一个以 .conf 结尾的文件
将动态库所在的目录拷贝到这个文件里面
再次运行程序
原因是我们没有更新配置文件,使用ldconfig命令将配置文件更新一下,更新之后系统就可以找到该可执行程序所依赖的动态库
[fy@VM-4-14-centos d7]$ sudo ldconfig
11.5 动态库的加载
静态库不需要加载,所以不考虑静态库的加载
静态库是程序在编译的时候把库的代码复制到可执行程序的地址空间的代码区中,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小比较大
优点:
使用静态库生成可执行程序后,该可执行程序就可以独自运行,不再需要库了
缺点:
使用静态库生成可执行程序会占用大量磁盘空间,特别是当有多个静态链接的程序同时运行时,这些静态链接的程序使用的都是很多相同的库,这时在内存当中就会存在大量的重复代码,造成内存浪费
动态库
动态库是程序在运行的时候才去链接相应的动态库代码的,多个程序共享使用库的代码
在可执行文件加载到内存时,所需要的动态库会从磁盘上加载到内存中,函数地址再由内存通过页表映射到这个可执行程序的共享区中,这个过程称为动态链接。动态库在多个程序间共享,节省了大量的磁盘空间和内存
优点:
节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。
缺点:
必须依赖动态库,否则无法运行
----------------我是分割线---------------
文章到这里就结束了,基础IO篇章结束,下一篇即将更新