Linux拓展:链接库
一.说明
本篇博客介绍Linux操作系统下的链接库相关知识,由于相关概念已在Windows下链接库一文中介绍,本篇博客直接上操作。
二.静态链接库的创建和使用
1.提前看
这里主要介绍的是C语言的链接库技术,而在Linux下实现C语言程序,需要你的虚拟机的WSL具有GCC编译器,如果你不了解,可以看Linux下实现C语言程序
2.静态链接库的创建
前提:准备好要编译的 .c 库文件和 .h 头文件,具体是是什么,请查看Windows下链接库
基本步骤:
- 1.执行
gcc -c myMath.c -o myMath.o
文件 - 2.使用ls命令查看上一步是否成功
- 3.使用
ar rcs libmyMath.a myMath.o
将所有目标文件打包为一个静态链接库(这里是以一个为例,命令下面介绍) - 4.使用ls命令查看上一步是否成功
没错,就这几步,你就简单的实现 静态链接库的建立了。
3.静态链接库的使用
静态链接库的用法很简单,就是和其它目标文件一起参与链接,最终生成一个可执行文件。
基本步骤:
- 1.同目录下再创建一个 main.c 文件
- 2.利用vim编辑器正常编写代码
- 3.利用gcc编译命令实现编译成.o文件
- 4.开始链接
第四步的链接命令有两种:
gcc -static main.o libmyMath.a -o main <-- 第 1 种
gcc main.o -o main -static -lmyMath -L /home/test/myMath <-- 第 2 种
上面两种如果不行,可以使用:
gcc -L /usr/lib main.o libmymath.a -o main
注意:里面的文件名和路径都要改成你自己文件名和路径,即不变色部分需要修改。
4.演示
三.动态链接库的创建与使用
1.提前看
Windows 平台上生成动态链接库时,需要用__declspec(dllexport)
显式地“告诉”编译器哪些函数和变量能被外界调用,这些函数和变量的信息(名称、存储位置)保存在引入库文件(.lib)中,而它们的定义保存在动态链接库文件(.dll)中。
与前者不同,Linux 平台上不再需要生成引入库文件,原因很简单,默认情况下动态链接库中定义的所有函数和变量都允许被外界调用。或者说,动态链接库中不仅保存了所有函数和变量的定义,还保存了能被外界调用的所有函数和变量的信息,所以不需要生成引入库文件。
2.动态链接库的建立
前提和静态链接库一样,需要一个.h文件和.c文件
基本步骤:
- 1.执行命令
gcc -shared -fPIC myMath.c -o libmyMath.so
- 2.使用ls命令查看上一步结果
有libmyMath.so文件即成功建立
3.动态链接库的使用
基本步骤:
- 1 将 main.c 与 myMath.h 放在同一目录,向 main.c 文件中引入 myMath.h 头文件
- 2.执行
gcc main.c libmymath.so -o main.exe
命令 - 3.使用ls命令查看上一步结果结果
4.补充注意
相比之下,你是不是以为和windows相比,Linux下动态链接库既然比静态链接库如此简单,那么你可以试着运行静态链接库和动态链接库最后的可执行文件,你会发现报错:./main.exe: error while loading shared libraries: libmyMath.so: cannot open shared object file: No such file or directory
这是因为执行main.exe文件时,需要调动所有动态链接库及其存储位置,而其中 libmyMath.so 文件的存储位置系统并不直到,有下面几种解决方式:
- 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64);
- 在终端输入
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中xxx
为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效); - 修改
~/.bashrc
或~/.bash_profile
文件,即在文件最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
(xxx
为动态库文件的绝对存储路径)。保存之后,执行source .bashrc
指令(此方式仅对当前登陆用户有效)。
接下来你重新运行即可成功。
5.演示
四.补充知识点
1.建立动态链接库
使用gcc -shared -fPIC myMath.c -o libmyMath.so
命令
gcc 命令中,各个选项的含义是:
- -shared:表示生成动态链接库;
- -fPIC:也可以写成 -fpic,功能是令 GCC 编译器生成动态链接库时,用相对地址表示库中各个函数和变量的存储位置。这样做的好处是,无论动态链接库被加载到内存的什么位置,都可以被多个程序(进程)同时调用;(无论怎样,你都加上)
- -o libmyMath.so:-o 选项用于指定生成文件的名称,此命令最终生成的动态链接库文件的文件名为 libmyMath.so。
Linux 平台上,动态链接库文件的命名格式为 libxxx.so,其中 xxx 部分可以自定义。
2.ar
ar
是一个用于创建静态库的命令行工具。
语法:ar rcs libname.a file1.o file2.o file3.o
其中 libname.a
是要创建的库的名称,.a
表示这是一个静态库文件。
file1.o
, file2.o
, file3.o
是包含目标代码的文件,你可以指定任意数量的文件来创建库。
r
, c
, 和 s
选项都是必须的,表示:
r
: 将新的目标文件添加到库中或者替换库中已有的同名文件。c
: 如果库不存在则创建它。s
: 在库中为每个成员生成符号表。
需要注意的是,Linux 平台上静态链接库的名称不是随意的,通常遵循 libxxx.a 格式,xxx 部分可以自定义。
五.动态链接库的显示调用
Linux 平台下,动态加载库的装载、使用、卸载等操作需要借助以下 4 个函数来完成,使用它们时需要引入<dlfcn.h>
头文件。
1.dlopen():打开库文件
语法:void *dlopen(const char *filename, int flag);
其中,filename
参数是共享库文件的路径名,可以是绝对路径或相对路径;flag
参数是一个标志位,用于指定 dlopen() 的行为,可以是以下值之一:
RTLD_LAZY
:表示在 dlopen() 返回句柄后,只有在需要时才解析符号。RTLD_NOW
:表示在 dlopen() 返回句柄前,解析所有符号。RTLD_GLOBAL
:表示将库中的符号添加到全局符号表中,以便其他库可以使用这些符号。RTLD_LOCAL
:表示不将库中的符号添加到全局符号表中,而是将其限制在当前库中。
成功调用 dlopen() 函数将返回一个非空的句柄,失败则返回 NULL。通过句柄可以使用 dlsym() 函数获取共享库中的符号地址,使用 dlclose() 函数关闭句柄并卸载共享库。
2. dlsym():从库文件中找到要调用的资源
语法:void *dlsym(void *handle, const char *symbol);
其中,handle
参数是由 dlopen() 返回的共享库句柄,symbol
参数是要查找的符号名称(字符串类型),可以是函数名、变量名或其他标识符。
如果成功找到该符号,则 dlsym() 将返回该符号的地址(指向函数或变量的指针)。否则,dlsym() 将返回 NULL。
3. dlclose():关闭打开的库文件
语法:int dlclose(void *handle);
其中,handle
参数是由 dlopen() 返回的共享库句柄。成功调用 dlclose() 函数将返回零,表示函数调用成功。失败则返回非零值,表示出现错误。
4.例子
#include <stdio.h>
#include <dlfcn.h>
int main() {
// 打开共享库
void *handle = dlopen("libmylib.so", RTLD_NOW);
if (handle == NULL) {
fprintf(stderr, "Error: %s\n", dlerror());
return 1;
}
// 查找符号
int (*my_func)(int) = (int (*)(int))dlsym(handle, "my_func");
if (my_func == NULL) {
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(handle);
return 1;
}
// 调用函数
int result = my_func(42);
printf("Result: %d\n", result);
// 关闭共享库
int close_result = dlclose(handle);
if (close_result != 0) {
fprintf(stderr, "Error: %s\n", dlerror());
return 1;
}
return 0;
}
上面的代码打开名为 libmylib.so
的共享库,查找其中名为 my_func
的函数,并调用该函数,最后关闭共享库。这里假设 libmylib.so
中包含一个名为 my_func
的函数,它接受一个 int 类型的参数并返回一个 int 类型的值。
需要注意的是,上面的代码中使用了 dlerror() 函数来获取错误信息。当 dlopen()、dlsym() 或 dlclose() 发生错误时,可以通过调用 dlerror() 函数来获取具体的错误信息。
六.总结
这就是Linux下链接库的知识了,为后面介绍makefile做基础,可以结合windows下链接库一起观看。