您的位置:首页 > 运维架构 > Linux

linux静态库与共享库(二)

2013-11-12 17:30 417 查看
原文地址:

/article/10289118.html


6 库的使用

下面来写个程序测试一下前面两个库文件,代码如下:

[cpp] view
plaincopyprint?

/* say.c */

#include <stdio.h>

#include <stdlib.h>

void say_something(const char *str);

int main(int argc, char **argv)

{

say_something(argv[1]);

exit(0);

}

下图中显示了生成的两个库文件:




静态库的使用

执行如下命令编译链接:

[plain] view
plaincopyprint?

gcc –c say.c –o say.o

gcc –o say say.o libsay.a

这将生成可执行文件say,./say加参数即可运行。这就是静态库,它只在链接时需要。

静态库使用很简单,但有一点需要注意,就是——链接顺序。如果你按如下顺序链接:

[plain] view
plaincopyprint?

gcc -o say libsay.a say.o

将得到这样的错误提示:

[plain] view
plaincopyprint?

say.o: In function `main':

say.c:(.text+0x15): undefined reference to `say_something'

collect2: ld returned 1 exit status

这是初学者经常碰到的问题,尤其当项目比较庞大库文件较多时。因为链接器(ld)按输入库顺序链接,libsay.a先输入,然后它发现say_something并未被使用,所以并不会链接,所以会报这样的错误。库文件的链接顺序尤为重要,其基本原则就是越独立越底层的库应尽量靠后放。


共享库的使用

共享库的使用有多种方法,我总结了五种,下面一一道来。

我们生成的共享库文件为 libsay.so.1.0.0,为了链接方便,我们用如下命令创建两个符号链接:

[plain] view
plaincopyprint?

ln -s libsay.so.1.0.0 libsay.so.1

ln -s libsay.so.1 libsay.so

创建后如图所示:



方法一:

[plain] view
plaincopyprint?

gcc say.c –o sayso –L. –Wl,-rpath=. –lsay

方法一用-L指定链接时搜索路径而用-rpath指定运行时搜索路径,编译后生成可执行文件sayso,可直接运行。

为了验证生成的sayso文件的依赖性,可用ldd(查看可执行文件的共享库依赖性)命令查看一下,如图所示:



我们还可以用 readelf 命令查看 rpath 即运行时路径:



在接下来的几种方法中我们同样可以用这两个命令查看依赖性。

方法二:

[plain] view
plaincopyprint?

export LD_LIBRARY_PATH=`pwd`

gcc –L. say.c –o sayso2 –lsay

方法二首先设置环境变量LD_LIBRARY_PATH为当前目录(小技巧,用`pwd`代替),因为共享库文件在当前目录下,这样运行可执行文件后也能找到该库文件。

方法三:

将共享库文件libsay.so.1.0.0复制到/usr/lib下,并创建 libsay.so libsay.so.1等符号链接(创建符号链接前面有提到),然后运行如下命令:

[plain] view
plaincopyprint?

gcc say.c –o sayso3 –lsay

从以上命令看到少了–L选项,因为/usr/lib是系统默认搜索路径,所以不必指定,但LD_LIBRARY_PATH对-l无效,所以方法二中依然需要–L选项。

为了验证方法三是可行的,我们可以把LD_LIBRARY_PATH重新置为空:

[plain] view
plaincopyprint?

export LD_LIBRARY_PATH=

然后再运行./sayso3发现运行正常,下图中显示了这种差异,由此也看出LD_LIBRARY_PATH的优先级别高于/usr/lib。



将/usr/lib中刚刚copy的文件删除,来试验一下方法四,这个时候运行./sayso3应该提示找不到库文件。

方法四:

接下来我们要用到一个文件——/etc/ld.so.conf(笔者的系统是ubuntu 10.04),这个文件记录了一些共享库搜索路径(或者完整的共享库名),查看其内容不难发现其用法,我们可以将我们的共享库所在路径添加到这里来,要使添加的路径有效,我们还必须以root权限执行ldconfig命令,该命令会根据ld.so.conf文件,在/etc目录下生成ld.so.cache文件,ld.so.cache文件才是系统运行时搜索库的关键。

cat ld.so.conf文件发现其内容如下:

include /etc/ld.so.conf.d/*.conf

所以我们只要在/etc/ld.so.conf.d目录下创建相应的conf文件即可。cd到该目录,用root权限创建 libsay.conf 文件,将共享库文件所在路径添加到该文件中保存,执行ldconfig。然后我们运行./sayso3,发现又能运行了。

方法五:

接下来是终极方法,也是非常有用有意思的方法!

方法五要用到系统库dl(专门服务于共享库加载),该库提供了以下几个函数:

dlopen: 将共享库载入内存,返回一个句柄

dlsym: 返回函数地址

dlclose: 从共享库中释放当前程序

dlerror: 返回出错字符串,如果没有则为空

dl库用法示例:

[cpp] view
plaincopyprint?

/* saydl.c */

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

int main(int argc, char **argv)

{

void (*say)(const char *);

char *error;

void *handle;

handle = dlopen("libsay.so", RTLD_LAZY);

if (error = dlerror()) {

printf("%s\n", error);

exit(1);

}

say = dlsym(handle, "say_something");

if (error = dlerror()) {

printf("%s\n", error);

exit(1);

}

say("hello, world!\n");

dlclose(handle);

exit(0);

}

编译这段代码:

[plain] view
plaincopyprint?

gcc saydl.c -o saydl -ldl

可以看到,在这里根本不需要指定 libsay.so 库相关路径,因为程序运行时通过dlopen动态加。虽然如此,程序运行还是会按照运行时库搜索优先级搜索的,除非dlopen采用了绝对路径。看到这个方法相信你会很惊讶,不要惊讶,请深入体会,这个方法很有价值。


7 总结

好了,如果你能从头到尾将这篇文章看完,并亲自动手实践,我相信你对于库的使用及相关问题应该游刃有余了,也说明你有足够的求知欲。从这里我们也可以看出linux高深的技术,这同时也是一种艺术,大神们为linux所做的工作令人叹服!

我查阅了大量资料以撰写该文章,请相信我这篇文章对你有用!这些看似烦琐细腻的技术,已给我带来极大的乐趣与帮助!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: