在使用redis写C代码的时候遇到的问题,安装redis时 make install可以看到:
mkdir -p/usr/local/include/hiredis /usr/local/lib
cp -a hiredis.h async.h read.h sds.h adapters/usr/local/include/hiredis
cp -a libhiredis.so/usr/local/lib/libhiredis.so.0.13
cd /usr/local/lib &&ln -sflibhiredis.so.0.13 libhiredis.so
cp -a libhiredis.a/usr/local/lib
把redis的头文件放进了/usr/local/include/hiredis,把动态库libhiredis.so和静态库libhiredis.a都放进了/usr/local/lib
于是我就在我的Makefile里面写:
gcc main.c -I/usr/local/include-L/usr/local/lib -lhiredis -o main
OK,gcc通过生成可执行文件main,但是一运行main报错:
error while loading shared libraries:
libhiredis.so.0.13: cannot open sharedobject file: No such file or directory
于是产生了两个疑问:
1. usr/local/lib 里面libhiredis.so和静态库libhiredis.a都有,为什么用的是.so而不是.a?gcc编译链接时写法都是-L库所在路径 -l库名,是否有写法可以区分指明要链接动态库还是静态库?
2. 我在Makefile里面不是已经指明so的位置,并且相应位置确实有libhiredis.so.0.13,为什么运行的时候还是找不到?
针对第一个问题,查了资料之后发现:
ld在链接的时候如果路径下相同库名的静态库和动态库都有,那么会优先使用动态链接库,动态链接库不存在才会使用静态链接库;
如果指明要使用静态链接库,那么需要在编译命令中加入-static参数;
gcc main.c -I/usr/local/include-L/usr/local/lib -lhiredis -static -o main
后来又做了测试发现,-static是一个全局参数,并不是只对一个链接生效,对所有的链接都生效。也就是一旦写上之后所有库都只会去找静态库,就不能指定某些库链静态库,而其它库链动态库;
针对第二个问题其实是很显然的,"link time"(链接时)和"runtime"(运行时),两个基本概念要弄清楚,不能混在一起。-L选项属于链接时,编译出来的可执行文件不知道-L选项后面的值,当然找不到对应的so。而-rpath,LD_LIBRARY_PATH都属于运行时
查资料之后发现运行的时候查找动态库路径顺序如下:
1. 可执行文件中自带的编译时写入的RPATH、RUNPATH
2. LD_LIBRARY_PATH 指定的地方
3. ldconfig 指定的地方:根据/etc/ld.so.cache查找
4. /lib
5. /usr/lib
所以相对应的解决办法有:
1. 使用rpath把so的路径指明并记录到二进制文件中
gcc main.c -I/usr/local/include -L/usr/local/lib -lhiredis –o main -Wl,--rpath=/usr/local/lib
重新编译再运行发现无报错了
关于RPATH、RUNPATH的详细相关作用方法见http://gotowqj.iteye.com/blog/1926771
2. 把libredis.so所在路径加入到LD_LIBRARY_PATH环境变量中
export LD_LIBRARY_PATH= /usr/local/lib:$LD_LIBRARY_PATH
3. 使用ldconfig修改/etc/ld.so.cache
ldconfig命令的用途主要是在默认搜寻目录/lib和/usr/lib以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件(一般默认为/etc/ld.so.cache),此文件保存已排好序的动态链接库名字列表
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。
由于我们是新安装了redis的动态链接库,因此需要手动把它加到缓存文件中去,具体做法为:
/etc/ld.so.conf下面加一行/usr/local/lib /usr/local/mysql/lib(需要root权限),保存过后ldconfig一下(需要root权限)
4. 把新安装的so拷到/lib或/usr/lib下
衍生问题:那么程序在链接so的时候ld到底做了些什么?(有待后续研究)