目录
问题起因
最近遇到一个问题,服务器上的程序一直起不来,gdb调试也会在中间“正常退出”。这个程序实际是一个主线程fork出一个守护进程。
排查过程
问题定位——gdb多进程调试
首先判断,可能是环境变量问题,于是查看LD_LIBRARY_PATH环境变量。这个环境变量用于设置动态库查找路径。将这个更新写进~/.bash_profile文件,这样在启动的时候就可以自动加载。但是在改完环境变量以后程序依然不能启动。于是尝试使用gdb调试以找出问题出现在什么地方。
开始调试的时候程序总显示“正常退出”,断点都没进。后来想起来了,这个是一个启动进程fork出守护进程的程序。于是上网查了一下,需要在gdb启动以后输入一下三条命令:
follow-fork-mode child # 设置gdb跟踪子进程
detach-on-fork on # 设置在发现fork的时候只跟踪一个进程
directory [源码路径] # 添加源码搜索路径
在使用gdb调试的时候发现程序在调用一段lua脚本的时候又显示“程序正常退出”,但是在日志打印中明明有lua脚本调用后的日志。这个“不科学”的现象直接让我裂开了(心态崩溃)。后来细心的我发现这个程序“正常退出”的时候gdb里面info inferiors #显示GDB调试的进程显示所有的进程都在执行ls这个shell命令。于是我查了程序,发现在lua里面调用了io.popen这个函数,而这个函数会调用一个系统命令,然后把回值作为标准输入。所以我猜想,是不是这个函数又fork了一下,并且用管道把回值传回来的。所以需要在调用lua脚本前先将gdb设置成跟踪父进程,这样就不会跑到新进程中去。于是执行了follow-fork-mode parent,果然就好了。
链接问题排查
使用gdb正确找出了问题的出现点,是在调用一个第三方库的时候崩溃的,经过一段时间的观察,我发现退出的时候总会显示一个信息exit with code 0177。上网搜了0177这个错误码,发现是在符号找不到的时候会报这个错,而且程序有的时候会报一个找不到“SSL_library_init”的符号缺失错误。于是就在程序里面找这个符号。于是使用最有力的工具ldd和nm。首先ldd发现动态库,但是nm却报错no symbols,我就很诧异,怎么一个动态库还能没有符号呢。于是又上网查,发现是因为符号被删除了,需要使用-D参数来显示动态链接符号(另外我发现-A参数可以显示出动态库的名字,虽然在一个库查找时候没啥用,但是在多个库中招符号时候非常好用)。我ldd发现程序链接的路径下的库确实没有这个符号。于是我想到了链接库查找顺序问题,经过资料查找,我知道了linux动态库查找的顺序是:
- 编译目标代码时指定的动态库搜索路径;
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
- 配置文件/etc/ld.so.conf中指定的动态库搜索路径;配置后要运行 ldconfig命令才能生效
- 默认的动态库搜索路径/lib;
- 默认的动态库搜索路径/usr/lib;
而LD_LIBRARY_PATH是已经配置过的,于是我想到了是不是使用了编译时的路径。后来我参考了安装手册(这个时候才发现还有个安装手册),发现需要将一些动态库拷贝到本地路径下。于是问题顺利解决了。
符号查找命令
现在附上符号查找的命令:
find . -name '*.so' | xargs nm -DA | grep '【需要查找的符号】'
看前面是T的符号来自哪个so,就是这个so提供了这个符号。
参考资料:
【1】linux 动态库搜索路径优先顺序以及Makefile 编译设置_起个名字是真难啊啊的博客-CSDN博客_linux的库查找路径的顺序
【2】nm 提示 no symbol 的问题(strip)_雷电羊的博客-CSDN博客_nm no symbols