目录
前言
大家有没有好奇一个问题,我们写代码时,都是包含头文件后直接调用printf
,scanf
等函数,那么代码是在哪里实现的呢?这就要涉及到库的知识。
一、动态库与静态库
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic
linking)- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
二、静态库生成步骤
2.1 生成静态库
当我们把 .o
和 .h
文件给别人时,是可以直接调用运行的,于是我们就可以把 .o
文件打包成 静态库 ,配合.h
文件一起使用。
如下我创建了add
与sub
方法,声明在.h
文件中,实现在.c
文件中,使用静态库打包.o
文件并编译。
[ydp@xxxx blog]# ls
add.c add.h main.c sub.c sub.h
[ydp@xxxx blog]# gcc -c add.c -o add.o
[ydp@xxxx blog]# gcc -c sub.c -o sub.o
//生成静态库
[ydp@xxxx blog]# ar -rc libmymath.a add.o sub.o
//ar意思是归档工具(打包)
//rc表示(replace and create)(更新与创建)
//查看静态库中的目录列表
[ydp@xxxx blog]# ar -tv libmymath.a
rw-r--r-- 0/0 1240 Apr 23 17:26 2023 add.o
rw-r--r-- 0/0 1240 Apr 23 17:26 2023 sub.o
//t:列出静态库中的文件
//v:verbose 详细信息
2.2 发布
当我们创建静态库之后,方便起见,会把静态库与头文件打包(拷贝)发布为一个目录,只需要把此目录就可以使用打包的方法。
mkdir -p lib-static/lib
mkdir -p lib-static/include
cp *.a lib-static/lib
cp *.h lib-static/include
三、动态库生成步骤
3.1 生成动态库
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so
生成动态库与静态库的方法基本相同,在生成.o
时加上-fPIC
,在编译时加上-shared
即可。
示例:
[ydp@xxxx blog]# gcc -fPIC -c sub.c add.c
[ydp@xxxx blog]# gcc -shared -o libmymath.so *.o
[ydp@xxxx blog]$ tree lib-dyl
lib-dyl
├── include
│ ├── add.h
│ └── sub.h
└── lib
└── libmymath.so
3.2 发布
动态库的发布方法与静态库相同
mkdir -p lib-dyl/lib
mkdir -p lib-dyl/include
cp *.a lib-dyl/lib
cp *.h lib-dyl/include
四、运行库
当我们打包好头文件和库之后,在使用者角度需要找到这些文件才能运行。由于静态库与动态库使用方法基本相同,先以静态库为例。
头文件搜索路径
- 在当前路径下查找头文件
- 在系统头文件路径下查找头文件
- 指定头文件搜索路径
4.1 在当前路径下查找头文件
由于我们直接使用发布的目录,头文件和库文件不在当前路径下,此方法不可行。
4.2 在系统头文件路径下查找头文件
- /usr/lib
- /usr/ocal/lb
只需要将自己的头文件和库文件拷贝到系统路径下即可。
gcc mytest.c -lmymath.h
l
:链接库,只要库名即可(去掉lib以及版本号)
由于拷贝到系统路径会污染系统库,此方法可行但不推荐。
4.3 指定头文件搜索路径
首先找到头文件所在路径,再找到库文件所在路径,并标识库的名称即可。
gcc mytest.c -o mytest -I./lib-static/include/ -L./lib-static/lib -lmymath
I
: 链接头文件所在的路径
L
:链接库所在的路径
在使用库时推荐此方法,如果觉得指令较长,只需在Makefile
中写一次。或当使用安全的第三方库时,只需安装到系统文件中,并使用 -l
选项指定库名称即可。
五、动态库使用差异
以上操作动态库都与静态库相同,但是在运行时会出现找不到库的问题。
使用 ldd 命令查看mytest
链接
ldd mytest
linux-vdso.so. l => (0x00007fff3c529000)
libmymath.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fec6935f000)
/lib64/ld-liunx-x86-64.so.2 (0x00007fec6972d000)
查看到动态库未找到
由于在编译时使用选项找到头文件和库,生成了对应的可执行程序,但是当运行程序时,找不到库文件。(给gcc找到文件和运行的程序无关)
那么运行静态库的时候,怎么没有这个问题呢?
形成可执行程序之后,已经把需要的代码拷贝进我的代码中。运行时,不依赖你的库,不需要运行时查找。
于是我们只需要想办法让程序找到动态库即可。
5.1 拷贝到系统路径下
拷贝.so
文件到系统共享库路径下, 一般指/lib64
5.2 导入环境变量
程序运行的时候,会在环境变量中查找自己需要的动态库路径-- LD_LIBRARY_PATH
,所以我们只需要在环境变量中手动添加动态库的路径即可。
//找到动态库的路径
[ydp@xxxx blog]# ls /home/ydp/blog/lib-dyl/lib/
libmymath.so
//将动态库的路径添加到环境变量中
[ydp@xxxx blog]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ydp/blog/lib-dyl/lib/
//使用echo可以发现库文件的路径已经添加到环境变量中
[ydp@xxxx blog]# echo LD_LIBRARY_PATH
:/home/ydp/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/ydp/blog/lib-dyl/lib/
//使用ldd可以发现库文件已经被链接
[ydp@xxxx blog]# ldd mytest
ldd mytest
linux-vdso.so. l => (0x00007fff3c529000)
libmymath.so => /home/ydp/blog/lib-dyl/lib/libmymath.so (0x00007f980bf01000)
libc.so.6 => /lib64/libc.so.6 (0x00007fec6935f000)
/lib64/ld-liunx-x86-64.so.2 (0x00007fec6972d000)
//运行程序成功
使用更改环境变量的方法可以使程序链接到动态库,但是重新启动Linux之后添加的环境变量失效。
5.3 更改系统配置文件
在Linux系统中,除了在环境变量查找动态库的路径,还有一种方式,就是在 /etc/ld.so.conf.d/
中直接查找配置文件。
我们可以在这个路径下创建自己的动态库配置文件来找到动态库。
//找到动态库路径
[ydp@xxxx blog]# ls /home/ydp/blog/lib-dyl/lib/
libmymath.so
//在/etc/ld.so.conf.d/创建一个配置文件
[ydp@xxxx blog]# sudo touch /etc/ld.so.conf.d/myconf.conf
//直接在配置文件中粘贴动态库路径(其余什么都不用写)
[ydp@xxxx blog]# sudo vim /etc/ld.so.conf.d/myconf.conf
[ydp@xxxx blog]# sudo cat /etc/ld.so.conf.d/myconf.conf
/home/ydp/blog/lib-dyl/lib/
//ldconfig命令使配置文件生效
[ydp@xxxx blog]# sudo ldconfig
//使用ldd可以发现库文件已经被链接
[ydp@xxxx blog]# ldd mytest
ldd mytest
linux-vdso.so. l => (0x00007fff3c529000)
libmymath.so => /home/ydp/blog/lib-dyl/lib/libmymath.so (0x00007f980bf01000)
libc.so.6 => /lib64/libc.so.6 (0x00007fec6935f000)
/lib64/ld-liunx-x86-64.so.2 (0x00007fec6972d000)
//运行程序成功
使用更改系统配置文件可以实现相同的效果,并且重新启动Linux之后还可以使用。
5.4 其他方法
之前几个方法都太麻烦了,有没有更人性化的方法呢?
可以在系统中创建动态库的软链接,让系统读取软连接从而找到库文件也是可行的。
//在系统路径下创建动态库的软链接
[ydp@xxxx blog]# sudo ln -s /home/ydp/blog/lib-dyl/lib/libmymath.so /lib64/libmymath.so
[ydp@xxxx blog]# ls /lib64/libmymath.so
/lib64/libmymath.so -> /home/ydp/blog/lib-dyl/lib/libmymath.so
//使用ldd可以发现库文件已经被链接
[ydp@xxxx blog]# ldd mytest
ldd mytest
linux-vdso.so. l => (0x00007fff3c529000)
libmymath.so => lib64/libmymath.so (0x00007f1a52ac000)
libc.so.6 => /lib64/libc.so.6 (0x00007fec6935f000)
/lib64/ld-liunx-x86-64.so.2 (0x00007fec6972d000)
//运行程序成功
推荐一些有趣的库
学习了如何创建并使用库,也可以试着使用一些别人制作的有趣的第三方库。
ncurses库(Linux下的图形界面库)
sudo yum install -y ncurses-devel
boost库
sudo yum install -y boost-devel
总结
本文分别详细介绍了动静态库的制作与使用,并且介绍了动态库与静态库的差异。大家也可以去尝试打包一下动静态库,这样可以更深入地了解其中的细节。喜欢的话,欢迎点赞支持和关注~