Linux下查看进程用到了哪些动态库的函数
Linux下查看进程用到了哪些动态库的函数
Linux下查看进程用到了哪些动态库的接口
在Linux下,查看进程使用的动态库及其接口,可以通过以下方法实现:
1. 查看进程加载的动态库文件
方法 1:通过 /proc/<PID>/maps 文件
1 | cat /proc/<PID>/maps | grep '\.so' |
- 说明:
列出进程内存映射中所有加载的.so动态库文件路径。
替换<PID>为实际进程ID。
方法 2:使用 pmap 命令
1 | pmap -x <PID> | grep '\.so' |
- 说明:
显示进程的内存映射,过滤出动态库路径。
方法 3:使用 lsof 命令
1 | lsof -p <PID> | grep '\.so' |
- 说明:
列出进程打开的文件,过滤出共享库文件(.so)。
2. 查看动态库导出的接口(符号)
方法 1:使用 nm 命令
1 | nm -D /path/to/library.so |
- 说明:
-D选项显示动态符号表(导出的接口)。
示例输出:1
20000000000003a60 T malloc
0000000000009400 T pthread_createT表示全局函数(即接口)。
方法 2:使用 objdump 命令
1 | objdump -T /path/to/library.so |
- 说明:
显示动态库的动态符号表(导出的函数接口)。
方法 3:使用 readelf 命令
1 | readelf -s --dyn-syms /path/to/library.so |
- 说明:
--dyn-syms选项列出动态符号(导出的接口)。
3. 实时监控进程调用的动态库接口
使用 ltrace 工具
1 | ltrace -p <PID> -l /path/to/library.so |
- 说明:
-p <PID>:附加到目标进程。-l lib.so:只监控特定库的调用(可省略)。
示例输出:显示进程调用的库函数及其参数。1
2libc.so.6->malloc(1024) = 0x55a6d5eeb260
libpthread.so.0->pthread_create(0x7ffd4a260d50, NULL, 0x55a6d5c8d1a0, NULL) = 0
4. 查看进程依赖的所有动态库
使用 ldd 命令(针对可执行文件)
1 | ldd /path/to/program |
- 注意:
ldd只能查看可执行文件的依赖库(非运行时进程)。
查看运行中进程的库依赖需结合/proc/<PID>/maps。
完整流程示例
假设进程ID为 1234,查看其使用的 libc 接口:
- 找到
libc库路径:1
2cat /proc/1234/maps | grep libc.so
# 输出示例:/usr/lib/x86_64-linux-gnu/libc.so.6 - 列出
libc的导出接口:1
2
3
4nm -D /usr/lib/x86_64-linux-gnu/libc.so.6 | grep ' T ' | head -n 5
# 输出示例:
# 00000000000a7c50 T malloc
# 00000000000a7ce0 T free - 实时监控进程调用:
1
ltrace -p 1234 -l libc.so.6
总结
| 目标 | 命令/方法 |
|---|---|
| 查看加载的动态库 | cat /proc/<PID>/maps | grep '\.so' |
| 列出动态库的导出接口 | nm -D /path/to/lib.so |
| 监控进程调用的库函数 | ltrace -p <PID> -l lib.so |
| 查看可执行文件的库依赖 | ldd /path/to/program |
注意:部分操作需
sudo权限(如监控非当前用户的进程)。
dlopen函数用法及示例
dlopen 函数详解
dlopen 是 Linux 动态链接库的核心函数,用于在运行时动态加载共享库(.so 文件)。它允许程序按需加载库,无需在编译时静态链接,常用于插件系统、模块化设计等场景。
函数原型
1 |
|
参数说明:
filename- 共享库路径(如
"libexample.so") - 若为
NULL,返回主程序自身的句柄(用于访问全局符号)。
- 共享库路径(如
**
flags**(常用标志组合)标志 作用 RTLD_LAZY延迟绑定(推荐):在首次调用函数时解析符号(提高启动速度)。 RTLD_NOW立即解析所有符号:加载时检查所有符号有效性,失败则返回错误。 RTLD_GLOBAL使库的符号全局可见(可供后续加载的库使用)。 RTLD_LOCAL符号仅对本库可见(默认)。
返回值:
- 成功:返回共享库的句柄(
void*类型) - 失败:返回
NULL,错误信息通过dlerror()获取。
关联函数
| 函数 | 作用 |
|---|---|
void* dlsym(void* handle, const char* symbol) |
从库中获取符号(函数/变量)地址 |
int dlclose(void* handle) |
关闭共享库(引用计数减为0时卸载) |
char* dlerror(void) |
获取最后一次错误信息(无错误返回 NULL) |
使用步骤
- 用
dlopen打开共享库 - 用
dlsym获取符号地址 - 调用符号(函数或访问变量)
- 用
dlclose关闭库 - 用
dlerror检查错误
示例 1:动态加载数学库(libm.so)
1 |
|
编译命令(需链接 dl 库):
1 | gcc -o math_demo math_demo.c -ldl |
示例 2:加载自定义共享库
步骤 1:创建共享库 libhello.so
1 | // hello.c |
编译为共享库:
1 | gcc -shared -fPIC -o libhello.so hello.c |
步骤 2:主程序动态加载 libhello.so
1 | // main.c |
编译并运行:
1 | gcc -o hello_demo main.c -ldl |
关键注意事项
路径问题
- 若库不在标准路径(如
/usr/lib),需使用绝对路径(如"/home/user/libhello.so")或相对路径(如"./libhello.so")。
- 若库不在标准路径(如
符号可见性
- 共享库中的函数需声明为
extern(默认)或使用编译选项-fvisibility=default。
- 共享库中的函数需声明为
错误处理
- 每次调用后检查
dlerror()(因连续调用会清除错误信息)。
- 每次调用后检查
资源释放
dlclose()减少引用计数,计数为 0 时卸载库(避免内存泄漏)。
C++ 兼容性
- C++ 函数需用
extern "C"避免名称修饰(Name Mangling):1
2
3extern "C" {
void say_hello(const char*);
}
- C++ 函数需用
常见错误场景
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
dlopen: file not found |
库路径错误 | 指定绝对路径或设置 LD_LIBRARY_PATH |
dlsym: undefined symbol |
函数名错误或未导出 | 用 nm -D lib.so 检查符号表 |
| 段错误(Segmentation Fault) | 函数指针类型不匹配 | 确保函数指针声明正确 |
进阶技巧
加载主程序符号
1
2void *self_handle = dlopen(NULL, RTLD_LAZY);
void *func = dlsym(self_handle, "local_function");控制符号可见性(GCC)
在库源码中声明导出符号:1
__attribute__((visibility("default"))) void public_func();
调试工具
- 使用
LD_DEBUG=libs环境变量跟踪加载过程:1
LD_DEBUG=libs ./your_program
- 使用
通过 dlopen 实现动态加载,可以显著提升程序的灵活性和可扩展性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!
