比女朋友更难找的是 libso

编译过程中,一个常见的报错是“找不到 lib*.so”,lib*.so 究竟为何物?该去哪里找呢?

*.so 是共享目标(Shared Object)文件,是经过编译但未链接的二进制文件,lib*.so 是库(Library)的共享目标文件,以动态链接的方式被调用,因而也叫动态链接库文件。在 Linux 系统中,系统公用的动态链接库文件存储在 /lib 目录和 /usr/lib 目录,在其他位置编译好的库文件,一般也会在这两个目录创建软连接。一些专用的动态链接库文件,也可以存储在软件自己的目录,放在 /home 或 /opt 等位置。

编译或运行程序的过程中经常遇到“找不到 lib*.so”或“lib*.so: No such file or directory”的报错。造成这个错误的原因一般有一下几点:1)确实没编译这些库文件,需要安装相应的软件包或者下载源码后编译;2)编译了这些库文件,但是编译或运行时找不到,一般是环境变量没配置好的原因,可以将编译好的库文件在 /usr/lib 或 /lib 创建软连接(需要 sudo 权限),或者将库文件所在目录添加到 LD_LIBRARY_PATH 环境变量(无需 sudo 权限);3)权限问题,编译好的库文件明明存在,环境变量也配置了,但仍然报错,此时应该检查文件权限,考虑用 chmod 更改权限或者 chown 更改所有者;4)文件结构问题,常见于机群,表现是登录节点可以运行,但是通过作业管理系统提交后,计算节点无法运行,此时应该将库文件拷贝到计算节点能够访问的路径。

遇到这类问题,首先要诊断原因,最常用的工具是 ldd。ldd 可以查看一个程序依赖的动态链接库的路径,例如

$ ldd $FOAM_APPBIN/icoFoam
        linux-vdso.so.1 (0x00007ffff9c92000)
        libfiniteVolume.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libfiniteVolume.so (0x00007fc680a3b000)
        libmeshTools.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libmeshTools.so (0x00007fc680470000)
        libOpenFOAM.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libOpenFOAM.so (0x00007fc67fae0000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc67f8b0000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc67f510000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc67f170000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc67ef50000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc67eb50000)
        libPstream.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/openmpi-system/libPstream.so (0x00007fc67e940000)
        libtriSurface.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libtriSurface.so (0x00007fc67e6a0000)
        libsurfMesh.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libsurfMesh.so (0x00007fc67e390000)
        libfileFormats.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libfileFormats.so (0x00007fc67e100000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc67dee0000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc682200000)
        libmpi.so.20 => /usr/lib/x86_64-linux-gnu/libmpi.so.20 (0x00007fc67dbe0000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc67d9c0000)
        libopen-rte.so.20 => /usr/lib/x86_64-linux-gnu/libopen-rte.so.20 (0x00007fc67d720000)
        libopen-pal.so.20 => /usr/lib/x86_64-linux-gnu/libopen-pal.so.20 (0x00007fc67d460000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fc67d250000)
        libhwloc.so.5 => /usr/lib/x86_64-linux-gnu/libhwloc.so.5 (0x00007fc67d010000)
        libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fc67ce00000)
        libnuma.so.1 => /usr/lib/x86_64-linux-gnu/libnuma.so.1 (0x00007fc67cbf0000)
        libltdl.so.7 => /usr/lib/x86_64-linux-gnu/libltdl.so.7 (0x00007fc67c9d0000)Code language: PHP (php)

可见,每一个 *.so 文件都有对应的位置。

然后,我们重命名 libmpi.so.20 为 libmpi.so.20~ 看看 ldd 的输出有什么变化。

$ sudo mv /usr/lib/x86_64-linux-gnu/libmpi.so.20 /usr/lib/x86_64-linux-gnu/libmpi.so.20~
$ ldd $FOAM_APPBIN/icoFoam
        linux-vdso.so.1 (0x00007fffe9bbe000)
        libfiniteVolume.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libfiniteVolume.so (0x00007f66890ab000)
        libmeshTools.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libmeshTools.so (0x00007f6688ae0000)
        libOpenFOAM.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libOpenFOAM.so (0x00007f6688150000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6687f20000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6687b80000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f66877e0000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f66875c0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f66871c0000)
        libPstream.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/openmpi-system/libPstream.so (0x00007f6686fb0000)
        libtriSurface.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libtriSurface.so (0x00007f6686d10000)
        libsurfMesh.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libsurfMesh.so (0x00007f6686a00000)
        libfileFormats.so => /home/phresher/OpenFOAM/OpenFOAM-2.4.x/platforms/linux64GccDPOpt/lib/libfileFormats.so (0x00007f6686770000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f6686550000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f668a800000)
        libmpi.so.20 => not found
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6686330000)Code language: PHP (php)

可见,libmpi.so.20 对应的结果成了 not found。

此时,可以试着用 apt-file 命令来查找软件源中包含 libmpi.so.20 的软件包。

$ sudo apt-file search libmpi.so.20
libopenmpi2: /usr/lib/x86_64-linux-gnu/libmpi.so.20
libopenmpi2: /usr/lib/x86_64-linux-gnu/libmpi.so.20.10.1
libopenmpi2: /usr/lib/x86_64-linux-gnu/openmpi/lib/libmpi.so.20.10.1Code language: JavaScript (javascript)

可见,libopenmpi2 在以下几个位置包含 libmpi.so.20。这里的文件并不是本地存在的,而是说安装了 libopenmpi2,会添加这几个文件。

我们可以试着用 apt 重新安装一下。

$ sudo apt --reinstall install libopenmpi2

再次运行 ldd 命令,可以发现,被我蓄意破坏的 libmpi.so.20 又被重新安装回来了。

$ !ldd
ldd $FOAM_APPBIN/icoFoam
...
        /lib64/ld-linux-x86-64.so.2 (0x00007f59bac00000)
        libmpi.so.20 => /usr/lib/x86_64-linux-gnu/libmpi.so.20 (0x00007f59b6530000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f59b6310000)
...Code language: PHP (php)

用 ldd 和 apt-file 可以安装好绝大多数 lib*.so。但有些时候问题并不在这儿。我最近接到一个朋友的咨询,安装好了 waves2Foam,可以在机群的登录节点正常运行,但是通过作业管理系统提交后报错退出,报错信息就是找不到某个 lib*.so,我当时第一反应是环境变量没设置好,提交作业时加了参数,将登录节点的环境变量全部同步到计算节点,结果还是不行。我 ssh 到计算节点发现计算节点的文件目录结构和登录节点不同,计算节点上确实是没有这个 lib*.so。我一时没了思路,放松一下后想到,机群中的每一个节点,都有一块本地的固态硬盘,而用户文件则存储在文件服务器(NAS)上,/lib 和 /usr/lib 等文件夹恰恰都是存在本地硬盘上的,不同节点并不能共享。一般情况,机群管理员会在所有节点上安装相同的包,以保证环境相同,使用频率较高的包会安装在本地的固态硬盘上,以保证速度;使用频率较低的包安装在文件服务器上,在局域网内共享。而在这个案例中,管理员并没有这么做,导致在登录节点编译出的程序依赖于 /lib 和 /usr/lib 中的动态链接库,而计算节点却没有这些文件。解决方案也很简单,只要把相应的动态链接库文件,从 /lib 和 /usr/lib 拷贝到 NAS 中 $LD_LIBRARY_PATH 包含的路径即可。

好了,libso 会有的,女朋友也会有的。

常恭

作者: 常恭

略懂 OpenFOAM

发表评论