# Linux下动态链接库 Linux 平台又是如何创建和使用动态链接库的呢?接下来,我们以 Ubuntu 系统上的 GCC 编译器为例,详细地讲解如何创建一个动态链接库,以及如何将某个动态链接库引入到我们自己的项目中。 > Linux 平台上,我们更习惯将动态链接库称为“共享库文件”或者“共享对象文件”,后缀名通常为 .so。 绝大多数 Linux 发行版都默认安装了 GCC 编译器,如果您不确定当前 Linux 发行版是否装有 GCC,打开 Terminal 命令行工具并执行`gcc --version`指令: test@ubuntu:~$ gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 这是笔者在 Ubuntu 系统上的执行结果,当前系统安装了 9.3.0 版本的 GCC 编译器。如果输出信息为: bash: /usr/bin/gcc: No such file or directory 表明当前系统没有安装 GCC,读者可以阅读《[GCC编译器下载和安装教程](http://c.biancheng.net/view/7933.html)》一文下载并安装 GCC 编译器。 ## 动态链接库的创建 Windows 平台上生成动态链接库时,需要用`__declspec(dllexport)`显式地“告诉”编译器哪些函数和变量能被外界调用,这些函数和变量的信息(名称、存储位置)保存在引入库文件(.lib)中,而它们的定义保存在动态链接库文件(.dll)中。 与前者不同,Linux 平台上不再需要生成引入库文件,原因很简单,默认情况下动态链接库中定义的所有函数和变量都允许被外界调用。或者说,动态链接库中不仅保存了所有函数和变量的定义,还保存了能被外界调用的所有函数和变量的信息,所以不需要生成引入库文件。 接下来,我们以 mymath.c 为例演示创建动态链接库的整个过程,如下为 myMath.h 和 myMath.c 文件中的内容: ``` //myMath.h//实现两个整数相加,返回它们的和int add(int a, int b);//实现两个整数相减,返回它们的差int sub(int a, int b);//实现两个整数相乘,返回它们的乘积int mul(int a, int b);//实现两个整数相除,返回它们的商int div(int a, int b);//myMath.c#include "myMath.h"int add(int a, int b) { return a + b;}int sub(int a, int b) { return a - b;}int mul(int a, int b) { return a * b;}int div(int a, int b) { if (b != 0) { return a / b; } return -1;} ``` Linux 平台上,用 GCC 编译器将 myMath.c 转换成动态链接库文件,只需要执行如下指令: test@ubuntu:~$ gcc -shared -fPIC myMath.c -o libmyMath.so gcc 命令中,各个选项的含义是: - -shared:表示生成动态链接库; - -fPIC:也可以写成 -fpic,功能是令 GCC 编译器生成动态链接库时,用相对地址表示库中各个函数和变量的存储位置。这样做的好处是,无论动态链接库被加载到内存的什么位置,都可以被多个程序(进程)同时调用; - -o libmyMath.so:-o 选项用于指定生成文件的名称,此命令最终生成的动态链接库文件的文件名为 libmyMath.so。 > Linux 平台上,动态链接库文件的命名格式为 libxxx.so,其中 xxx 部分可以自定义。 我们也可以先将 myMath.c 编译为目标文件,然后再将目标文件转换为动态链接库,整个过程为: test@ubuntu:~$ gcc -c -fPIC myMath.c <- 转换为目标文件 test@ubuntu:~$ ls myMath.c myMath.o test@ubuntu:~$ gcc -shared myMath.o -o libmyMath.so <- 生成动态链接库 test@ubuntu:~$ ls myMath.c myMath.o libmyMath.so > 实际上以上两种转换过程是一样的,直接将 myMath.c 转换成动态链接库的过程,底层也是先将 myMath.c 转换为 myMath.o,然后再将 myMath.o 转换为动态链接库。 由此,我们就成功地创建了动态链接库文件。 ## 动态链接库的使用 以刚刚生成的 libmyMath.so 为例,讲解 Linux 平台上如何将一个动态链接库文件引入到我们自己的项目中。 假设某个项目中仅有一个 main.c 文件,包含的代码如下: ``` #include int main() { int a = 3, b = 4; printf("a+b=%d\n", add(a, b)); printf("a-b=%d\n", sub(a, b)); printf("a*b=%d\n", mul(a, b)); printf("a/b=%d", div(a, b)); return 0;} ``` 显然,程序中用到了 libmyMath.so 动态链接库中的函数。执行以下两步,即可完成 main.c 和 libmyMath.so 的动态链接: \1) 将 main.c 与 myMath.h 放在同一目录,向 main.c 文件中引入 myMath.h 头文件: ``` #include "myMath.h" ``` \2) 执行如下指令,即可生成可执行文件: test@ubuntu:~$ gcc main.c libmymath.so -o main.exe test@ubuntu:~$ ls libmymath.so myMath.h main.c main.exe myMath.c 最终生成的 main.exe 就是可执行文件。 注意,main.exe 执行时需要将 libmyMath.so 一起载入内存,您运行 main.exe 时可能会遇到如下问题: test@ubuntu:~$ ./main.exe ./main.exe: error while loading shared libraries: libmyMath.so: cannot open shared object file: No such file or directory 执行结果提示:main.exe 执行时无法找到 libmyMath.so 动态链接库。通过执行 ldd main.exe 指令,可以查看 main.exe 执行时需要调用的所有动态链接库,以及它们各自的存储位置: test@ubuntu:~$ ldd main.exe linux-vdso.so.1 (0x00007fff353ef000) libmyMath.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fef8c9fd000) /lib64/ld-linux-x86-64.so.2 (0x00007fef8cc07000) 可以看到,main.exe 执行时需要链接 4 个动态库文件,其中 libmyMath.so 文件的存储位置显示“not found”,是导致 main.exe 执行失败的直接原因。 运行由动态链接库生成的可执行文件时,必须确保程序运行时可以找到所有需要的动态链接库。常用的解决方案有如下几种: - 将链接库文件移动到标准库目录下(例如 /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`指令(此方式仅对当前登陆用户有效)。 例如选择第一种解决方案,将 libmyMath.so 移动到 /usr/lib 目录下,再次执行`ldd main.exe`指令: test@ubuntu:~$ ldd main.exe linux-vdso.so.1 (0x00007ffc51fd0000) libmyMath.so => /lib/libmyMath.so (0x00007fe2038d1000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe2036df000) /lib64/ld-linux-x86-64.so.2 (0x00007fe2038ee000) 重新执行 main.exe : test@ubuntu:~$ ./main.exe a+b=7 a-b=-1 a*b=12 a/b=0 可以看到,main.exe 执行成功了,表明我们成功地将 libmyMath.so 引入到了自己的项目中。