您当前的位置:首页 > 计算机 > 编程开发 > 编程箴言

Linux下动态链接库的创建和使用

时间:01-21来源:作者:点击数:

继《Windows下动态链接库的创建和使用》一节后,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编译器下载和安装教程》一文下载并安装 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 <stdio.h>
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:xxxxxx为动态库文件的绝对存储路径)。保存之后,执行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 引入到了自己的项目中。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门