动态库的制作与两种使用方式你掌握了吗?

365bet规则 2026-11-05 18:48:25 admin 5669 674
动态库的制作与两种使用方式你掌握了吗?

前言在《如何制作属于自己的静态库》中简单介绍了静态库的制作方法,但实际上动态库的使用更为广泛,至于原因,在《静态库和动态库的区别》一文中已有说明。本文介绍动态库的制作方法以及两种使用方式。

示例程序test.c代码如下:

代码语言:javascript复制//来源:公众号【编程珠玑】 网站:https://www.yanbinghu.com

#include"test.h"

void test()

{

printf("I am test;hello,编程珠玑\n");

}

test.h代码如下:

代码语言:javascript复制#include

void test();

代码比较简单,只有一个test函数,用于打印一段字符串。

制作动态库只需要执行以下命令即可:

代码语言:javascript复制$ gcc test.c -fPIC -shared -o libtest.so

其中的-fPIC表示生成位置无关代码,以便在只有一个副本的情况下供多个应用程序共享。

通过readelf命令查看elf文件类型:

代码语言:javascript复制$ readelf -h libtest.so

ELF Header:

Magic: f c

Class: ELF64

Data: 's complement, little endian

Version: (current)

OS/ABI: UNIX - System V

ABI Version:

Type: DYN (Shared object file)

Machine: Advanced Micro Devices X86-64

从结果中可以看到,libtest.so为Shared object file。

使用动态库常见有两种使用方式,一种是加载时链接,另一种是使用时链接。

来源:公众号【编程珠玑】

个人博客:https://www.yanbinghu.com

未经授权禁止以任何形式转载

加载时链接加载时链接在代码中不需要做额外的动作,像使用静态库一样使用即可。例如main.c如下:

代码语言:javascript复制//来源:公众号【编程珠玑】 网站:https://www.yanbinghu.com

#include

#include"test.h"

int main(void)

{

test();

return ;

}

编译链接:

代码语言:javascript复制$ gcc -o main main.c -L . -ltest

其中-L指定从当前目录下寻找动态库libtest.so,否则会找不到。

然后我们还可以通过ldd命令查看其依赖的动态库:

代码语言:javascript复制$ ldd main

linux-vdso.so.1 => (0x00007ffd57757000)

libtest.so => not found

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f84c13f6000)

/lib64/ld-linux-x86-64.so.2 (0x00007f84c17c0000)

其中就有我们自己制作的libtest.so。

运行:

代码语言:javascript复制$ ./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

很不幸,程序并没有如预期的那样运行起来,而是报错了。这是为什么呢?

其实我们在使用ldd命令查看的时候,就注意到:

代码语言:javascript复制libtest.so => not found

它并不能找到这个动态库,因为它会默认从系统库的路径去查找这个库,但是我们并没有把这个库放到系统路径下,因此会找不到了。

我们有两种方法解决这个问题:

将libtest.so库放到系统路径下指定当前进程动态库搜索路径第一种方法:

代码语言:javascript复制$ cp libtest.so /usr/lib

$ ./main

I am test;hello,编程珠玑

第二种方法:

代码语言:javascript复制$ export LD_LIBRARY_PATH=./

$ ./main

I am test;hello,编程珠玑

导入LD_LIBRARY_PATH环境变量,指定库搜索路径,使得main程序能够找到libtest.so。

此时再看:

代码语言:javascript复制$ ldd main

linux-vdso.so.1 => (0x00007ffcdebdf000)

libtest.so => ./libtest.so (0x00007f494a45f000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f494a095000)

/lib64/ld-linux-x86-64.so.2 (0x00007f494a661000)

libtest.so不再是not found了。

使用时链接为了使用这种方式,需要使用几个函数dlopen,dlsym,dlclose,dlerror,其原型分别如下:

代码语言:javascript复制#include

void *dlopen(const char *filename, int flags);

void *dlsym(void *handle, const char *symbol);

int dlclose(void *handle);

char *dlerror(void);

其中dlopen用于打开一个动态库,filename是动态库的名称,flags是打开标志,一般为RTLD_LAZY,表示当要调用的时候才去解析符号;而RTLD_NOW则在dlopen之前就会去解析,还有其他选项这里就不多介绍了。

dlsym函数用于从动态库中查找需要使用的函数;

dlclose函数用于卸载已加载的动态库;

dlerror函数用于打印动态库相关错误。

我们修改main.c,来看看具体如何使用:

代码语言:javascript复制//来源:公众号【编程珠玑】 网站:https://www.yanbinghu.com

#include

#include

#include"test.h"

int main(void)

{

printf("start to call test\n");

char *error = NULL;

/*打开动态库*/

void *handle = dlopen("libtest.so",RTLD_LAZY);

if(NULL == handle)

{

error = dlerror();

printf("open error:%s\n",error);

return -1;

}

/*返回类型为函数指针*/

void (*fun)() = dlsym(handle,"test");

if(NULL == fun)

{

error = dlerror();

printf("open error:%s\n",error);

dlclose(handle);

return -1;

}

/*调用函数*/

(*fun)();

/*关闭*/

dlclose(handle);

printf("end to call test\n");

return ;

}

这种方式的动态库使用可以大致分为以下几个步骤:

使用dlopen打开动态库使用dlsym找到需要使用的符号调用动态库中的函数dlopen关闭(卸载)动态库在文本的代码中,用到了函数指针,相关内容可参考《高级指针话题-函数指针》。

编译运行:

代码语言:javascript复制$ gcc main.c -ldl -L . -o main #需要链接libdl.so库

$ ./main

start to call test

open error:libtest.so: cannot open shared object file: No such file or directory

运行时,我们发现并没有如预期的那样。但是可以看到,程序已经打印了start to call test,然后才报错,说明程序是在运行起来之后再尝试去从动态库中查找test符号的。

当然了,至于问题原因,我们在前面已经提到了,是由于没有设置动态库搜索路径或者在系统默认库路径下没有我们需要的libtest.so。按照前面提供的两种方法修正后,重新运行:

代码语言:javascript复制$ ./main

start to call test

I am test;hello,编程珠玑

end to call test

一切正常。

这种方式有以下好处:

编译时无需链接需要的动态库,我们注意到第二种方式编译时没有加-ltest如果程序的某些场景不需要动态库的函数,那么它就不会去加载该动态库再看动态库如果我们修改test.c的代码,我们不再需要重新编译main.c,而只需要更新动态库即可。

假如我们将test.c代码中的打印语句修改为如下:

代码语言:javascript复制//来源:公众号【编程珠玑】 网站:https://www.yanbinghu.com

#include"test.h"

void test()

{

printf("hello www.yanbinghu.com\n");

}

重新生成动态库libtest.so,重新执行main:

代码语言:javascript复制$ ./main

start to call test

hello www.yanbinghu.com

end to call test

我们发现,不需要重新编译main进程,就可以达到替换test函数实现的效果。

总结动态库应用广泛,其制作过程可能不作深入要求,但是其基本使用还是非常有必要了解的。本文总结如下:

程序运行时不能脱离动态库动态库有两种常见使用方式,一种是加载是链接,一种是运行时链接只要函数声明没有改变,动态库中函数实现的更新不需要重新编译可执行文件

相关推荐

生命鲈鱼哪里多
365娱乐app官方版下载

生命鲈鱼哪里多

08-20 100
盎司到毫升转换器
365bet规则

盎司到毫升转换器

11-28 944