在Linux系统中,SO库(Shared Object,共享库)是一种可被多个程序动态加载和调用的二进制文件,类似于Windows系统中的DLL文件,它通过代码复用减少了内存占用,便于模块化开发和程序维护,本文将详细介绍Linux下SO库的创建、使用及调试方法。
SO库的创建
创建SO库需先将源代码编译为位置无关代码(PIC),再链接生成共享库,假设有两个源文件add.c
和sub.c
,分别实现加法和减法函数:
// add.c int add(int a, int b) { return a + b; } // sub.c int sub(int a, int b) { return a - b; }
编译步骤如下:
-
生成目标文件(添加-fPIC选项):
gcc -fPIC -c add.c -o add.o gcc -fPIC -c sub.c -o sub.o
-fPIC
确保代码可被加载到任意内存地址,是共享库的必要选项。 -
链接生成SO库:
gcc -shared -o libmymath.so add.o sub.o
-shared
指定生成共享库,libmymath.so
是库文件名(Linux下共享库通常以lib
开头,.so
。
SO库的使用方式
SO库的使用分为静态链接(编译时链接)和动态链接(运行时加载)两种。
静态链接(编译时链接)
在编译可执行文件时,通过-L
(指定库路径)和-l
(指定库名)选项链接SO库,使用libmymath.so
的main.c
:
// main.c #include <stdio.h> extern int add(int, int); extern int sub(int, int); int main() { printf("3+5=%dn", add(3, 5)); printf("3-5=%dn", sub(3, 5)); return 0; }
编译命令:
gcc main.c -L. -lmymath -o main
-L.
:表示当前目录存在SO库(若库在/usr/lib
等系统路径,可省略-L
)。-lmymath
:链接libmymath.so
(去掉lib
前缀和.so
后缀)。
运行时,需确保SO库在系统默认路径(如/usr/lib
)或通过LD_LIBRARY_PATH
指定路径:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH # 添加当前路径到动态库搜索路径 ./main
动态链接(运行时加载)
通过dlopen
、dlsym
、dlclose
等函数在程序运行时动态加载SO库,适用于插件化开发,修改main.c
如下:
// main.c (动态加载版) #include <stdio.h> #include <dlfcn.h> int main() { void *handle = dlopen("./libmymath.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "dlopen error: %sn", dlerror()); return 1; } int (*add_ptr)(int, int) = dlsym(handle, "add"); int (*sub_ptr)(int, int) = dlsym(handle, "sub"); if (!add_ptr || !sub_ptr) { fprintf(stderr, "dlsym error: %sn", dlerror()); dlclose(handle); return 1; } printf("3+5=%dn", add_ptr(3, 5)); printf("3-5=%dn", sub_ptr(3, 5)); dlclose(handle); return 0; }
编译时需链接dl
库:
gcc main.c -ldl -o main_dynamic
运行时无需预先设置LD_LIBRARY_PATH
(除非SO库不在默认路径),直接执行./main_dynamic
即可。
SO库的查看与调试
查看依赖库
使用ldd
命令检查可执行文件依赖的共享库:
ldd ./main
输出示例:
linux-vdso.so.1 => (0x00007ffc...)
libmymath.so => ./libmymath.so (0x00007f8c...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8c...)
/lib64/ld-linux-x86-64.so.2 (0x00007f8c...)
查看库符号
使用nm
命令查看SO库中的导出符号(函数/变量):
nm -D libmymath.so
输出示例:
0000000000001120 T add
0000000000001130 T sub
U __libc_start_main
U printf
T
表示全局符号,U
表示未定义符号(需依赖其他库)。
更新共享库缓存
将SO库复制到系统库路径(如/usr/lib
)后,需运行ldconfig
更新缓存:
sudo cp libmymath.so /usr/lib/ sudo ldconfig
之后无需设置LD_LIBRARY_PATH
即可直接运行程序。
静态链接与动态链接对比
特性 | 静态链接 | 动态链接 |
---|---|---|
加载时机 | 编译时 | 运行时 |
文件大小 | 可执行文件较大(包含库代码) | 可执行文件较小(仅包含链接信息) |
更新维护 | 需重新编译程序 | 仅需替换SO库,程序无需重新编译 |
依赖管理 | 无外部依赖 | 需确保SO库存在于系统路径 |
内存占用 | 多个程序无法共享库代码 | 多个程序共享同一份库代码,节省内存 |
相关问答FAQs
Q1: 运行时报错“cannot open shared object file: No such file or directory”怎么办?
A: 此错误表示程序找不到SO库,解决方法:
- 检查SO文件是否存在(如
libmymath.so
); - 使用
export LD_LIBRARY_PATH=库路径:$LD_LIBRARY_PATH
临时添加库路径; - 将SO库复制到系统默认路径(如
/usr/lib
)并运行sudo ldconfig
更新缓存; - 检查
ldd
输出中库路径是否正确。
Q2: 编译时报错“undefined reference to”符号错误,如何解决?
A: 此错误通常是因为未正确链接SO库或符号未导出,解决方法:
- 确认编译时添加
-l库名
选项(如-lmymath
); - 检查SO库是否使用
-shared
生成,且目标文件编译时添加了-fPIC
; - 使用
nm -D lib库名.so
检查符号是否存在,若符号未导出(显示为U
),需在源代码中使用__attribute__((visibility("default")))
修饰函数,或检查链接脚本设置。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/23924.html