linux下动态库的生成和链接(.so)
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
windows和linux下都存在库,但不同。
静态库:.a 文件。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
动态库:.so,也叫共享库(shared object)。程序运行时的动态链接,多个进程可以链接同一个共享库。动态库在程序编译时并不会被连接到目标代码中,程序运行时被载入,因此在程序运行时还需要动态库存在。
优点:一旦动态库中的函数发生变化,对于可执行程序来说是透明的,可执行程序无需重新编译。这对于程序的发布、维护、更新起到了积极的作用。
当静态库和动态库同名时, gcc命令将优先使用动态库
以后慢慢研究原理,先实现一个最简单的库链接。
LINUX下 so文件即为动态库,类似于windows下的DLL。
作用:1、代码复用,简化代码,节约资源
2、不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例
作用:1、代码复用,简化代码,节约资源
2、不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例
//test.h
#include <stdio.h>
int a();
int b();
//test.c
#include "test.h"
int a()
{
printf("hello world\n");
return 0;
}
int b()
{
printf("welcome to China\n");
return 0;
}
//main.c
#include "test.h"
int main()
{
a();
b();
printf("program excute success\n");
return 0;
}
编译生成 libtest .so 文件,并通过链接库编译生成可执行文件 mm
gcc test.c -fPIC -shared -o libtest.so
gcc main.c -L. -ltest -o mm
gcc main.c -L. -ltest -o mm
-shared:该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
执行:./mm
./mm: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
./mm: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
执行出错了,原因是操作系统无法找到库。
查看一下依赖的库:ldd mm
ldd mm
linux-vdso.so.1 => (0x00007fffc01ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0592b06000)
/lib64/ld-linux-x86-64.so.2 (0x00007f05930af000)
查看一下依赖的库:ldd mm
ldd mm
linux-vdso.so.1 => (0x00007fffc01ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0592b06000)
/lib64/ld-linux-x86-64.so.2 (0x00007f05930af000)
发现libtest.so并没有链接进去
解决方法一:
1、将.so文件放入默认搜索路径中。一般在:/etc/lib/.下
2、在多用户环境下,一般我们不享有默认路径写入的权限。可以通过设置 LD_LIBRARY_PATH 环境变量解决:
export LD_LIBRARY_PATH=.
或者: export LD_LIBRARY_PATH=$(pwd)
这里的意思是将当前文件添加为共享目录。当然,后面的目录可以自己手动输入绝对路径
解决方法一:
1、将.so文件放入默认搜索路径中。一般在:/etc/lib/.下
2、在多用户环境下,一般我们不享有默认路径写入的权限。可以通过设置 LD_LIBRARY_PATH 环境变量解决:
export LD_LIBRARY_PATH=.
或者: export LD_LIBRARY_PATH=$(pwd)
这里的意思是将当前文件添加为共享目录。当然,后面的目录可以自己手动输入绝对路径
按照这样设置后,在执行可执行文件时,操作系统会先在LD_LIBRARY_PATH下搜索库文件,搜索不到再去默认路径中继续搜索。
缺点:修改环境变量,会影响所有的可执行程序。如果我们在编译其他程序时,如果我们不小心,很可能导致其他可执行文件无法运行。
缺点:修改环境变量,会影响所有的可执行程序。如果我们在编译其他程序时,如果我们不小心,很可能导致其他可执行文件无法运行。
解决方法二:提供 -rpath选项(runtime path)
将搜索路径信息写入可执行文件,不需要设置环境变量。
缺点:当库文件移动时,需要重新编译生成可执行文件。
编译命令:
gcc -g -o mm main.c -ltest -L. -Wl,-rpath=.
-Wl(大写的W,小写的l)
-rpath选项是传递给连接器(linker),“.”表示当前目录
将搜索路径信息写入可执行文件,不需要设置环境变量。
缺点:当库文件移动时,需要重新编译生成可执行文件。
编译命令:
gcc -g -o mm main.c -ltest -L. -Wl,-rpath=.
-Wl(大写的W,小写的l)
-rpath选项是传递给连接器(linker),“.”表示当前目录
ldd mm
linux-vdso.so.1 => (0x00007fffc01ff000)
libtest.so => /opt/code/xqf/dcs/src/linux_practice/libtest.so (0x00007f0592eac000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0592b06000)
/lib64/ld-linux-x86-64.so.2 (0x00007f05930af000)
执行:./mm
hello world
welcome to China
program excute success
hello world
welcome to China
program excute success
成功!!
文件列表:ll
-rwxr-xr-x 1 root root 1936 Apr 20 08:31 libtest.a
-rwxr-xr-x 1 root root 8200 Apr 18 22:21 libxqf.so
-rwxr-xr-x 1 root root 105 Apr 18 22:19 main.c
-rwxr-xr-x 1 root root 136 Apr 18 22:19 test.c
-rwxr-xr-x 1 root root 38 Apr 18 19:41 test.h
-rwxr-xr-x 1 root root 1792 Apr 19 19:30 test.o
-rwxr-xr-x 1 root root 12410 Apr 18 22:24 mm
-rwxr-xr-x 1 root root 1936 Apr 20 08:31 libtest.a
-rwxr-xr-x 1 root root 8200 Apr 18 22:21 libxqf.so
-rwxr-xr-x 1 root root 105 Apr 18 22:19 main.c
-rwxr-xr-x 1 root root 136 Apr 18 22:19 test.c
-rwxr-xr-x 1 root root 38 Apr 18 19:41 test.h
-rwxr-xr-x 1 root root 1792 Apr 19 19:30 test.o
-rwxr-xr-x 1 root root 12410 Apr 18 22:24 mm
./mm & 表示在后台执行该可执行文件
几点知识, 参考资料:
静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径;
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
4. 默认的动态库搜索路径/lib, /usr/lib, /usr/local/lib
1. 编译目标代码时指定的动态库搜索路径;
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
4. 默认的动态库搜索路径/lib, /usr/lib, /usr/local/lib
新安装一个库之后如何让系统能够找到它
如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。
如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下
1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
2.运行ldconfig,该命令会重建/etc/ld.so.cache文件
如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。
如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下
1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
2.运行ldconfig,该命令会重建/etc/ld.so.cache文件
编译生成静态库:
gcc -c test.c 生成 test.o 文件
ar crv libtest.a test.o 创建静态库文件 libtest.a
gcc -o mm main.c -static -L. -ltest 生成可执行文件 mm
执行:./mm
hello world
welcome to China
program excute success