读书频道 > 网站 > 网页设计 > 嵌入式系统: 工程案例教程
2.4.3 创建和使用库
12-11-13    奋斗的小年轻
收藏    我要投稿   

本文所属图书 > 嵌入式系统: 工程案例教程

嵌入式系统是以应用为中心、计算机技术为基础,软、硬件可剪裁,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。本书共6章:第1章介绍几种嵌入式处理器,包括8051单片机、AVR单片机、A...立即去当当网订购

库是操作系统运行的基础,也是程序发布时重要的组成部分,使用库可以实现代码复用,提高编程效率。库文件本质上是二进制格式的可执行代码,加载到内存中就可运行。库分为静态库和动态库两种,静态库是指在程序编译时即将库编译至可执行程序中,每个可执行程序中都有相关库的一个副本,程序可以脱离库的环境独立运行;动态库是指编译生成的可执行文件中并不包括库文件,只有当程序执行时才临时链接库文件。静态库和动态库二者特点不同,使用静态库编译的程序可以脱离库文件直接运行,但是文件体积较大,而且当库文件修改后,可执行程序需要重新链接,使用较为烦琐;使用动态库编译的程序恰好相反,程序体积小,库文件更新后只要导出的接口不变,可执行程序就无须重新编译,可以直接使用,但是动态编译的程序运行时无法脱离库环境,不能独立运行。

1.创建库

下面以test.c和main.c两个文件为例,介绍静态库和动态库的创建方法。

/*test.c */

# include <stdio.h>

void test()

{

  printf("This is a test");

  return;

}

/*main.c,调用test.c文件中的test()函数 */

# include <stdio.h>

# include <math.h>

int main()

{

  float x = 45;

  printf("sin(x) = %f",sin(x));

  test();

  return 0;

}

在Linux下创建库分为两个步骤:第1步,把源文件编译成.o文件;第2步,把目标文件.o链接成库文件,这里的库可以是静态库或动态库。下面以test.c为例,按照上述步骤创建库文件。

(1)将.c源文件编译成.o目标文件

无论静态库还是动态库,都必须先生成.o文件,因此先用-c选项编译源程序test.c,命令如下:

$gcc -c test.c

上述命令中,“-c”选项表示只进行预处理、编译和汇编,并不进行链接。在不指定输出文件名的情况下,系统默认将源文件扩展名.c替换为.o,以此命名目标文件,因此,上述命令实际相当于:

$gcc -c test.c -o test.o

(2)由.o文件创建静态库

静态库文件名命名格式以lib为前缀,紧接着是实际静态库名,之后是扩展名.a。例如,将创建的静态库命名为demo,则静态库文件名为libdemo.a,命令如下:

$ar -src libtest.a test.o

创建静态库时使用ar命令。其中,选项-r表示在库libtest.a中插入test.o定义的相关模块,如果插入的模块名已经在库中存在,则替换同名的模块;选项-s表示创建目标文件索引;选项-c表示创建库文件libtest.a,无论文件是够存在,都将创建。

(3)由.o文件创建动态库

动态库文件命名格式和静态库文件名命名格式类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如,将创建的动态库命名为demo,则动态库文件名为libdemo.so,命令如下:

$gcc -shared -fPIC test.o -o libtest.so

创建动态库时使用gcc命令。其中,选项“-shared”是必需的,它表示生成的是动态库,而不是静态库;-fPIC选项表示创建的动态库文件是位置无关的(Position-Independent Code,PIC),即在可执行程序装载这些动态库文件时,可将它们放在可执行程序内存里的任何地方。

2.使用库

在Linux中,库文件一般位于/usr/lib或者/lib目录下,如果用户没有特别指明被调用库的路径,gcc会到系统默认的路径/usr/lib下查找这些库文件。

(1)使用静态库

静态库创建成功之后,即可以使用它内部的公共函数,只需要在使用到这些公用函数的源程序中,包含这些公用函数的原型声明,并在用gcc命令生成可执行文件时指明静态库名,gcc就会从该静态库中把公用函数链接到可执行文件中。注意,gcc会自动在静态库名前加上前缀lib,然后在静态库名后追加扩展名.a,得到的静态库文件名,以此来查找静态库文件,具体操作如下:

$ls

libtest.a main.c test.c test.o

$gcc -o main main.c -L . -ltest -lm

$./main

sin(x)=0.850904 This is a test

其中,-L.是将当前目录添加到库文件的搜索路径列表中,-ltest表示在指定的搜索目录列表中查找libtest.a库文件,并且与main.c进行编译链接,最终生成可执行文件main。

现在删除静态库文件libtest.a,测试静态库的函数test()是否真正链接到了可执行文件main中,命令如下:

$rm libtest.a

rm:remove regular file ‘libtest.a’?y

$ls

main main.c test.c test.o

$./main

sin(x)=0.850904 This is a test

程序仍可正常运行,说明静态库文件中的函数确实已链接到可执行文件中。

(2)使用动态库

程序中使用动态库和使用静态库的步骤完全一样,也需要在源程序中包含这些公用函数的原型声明,然后在用gcc命令生成可执行文件时,指明动态库名进行编译。注意,gcc会在动态库名前加前缀lib,然后在动态库名后追加扩展名.so,得到动态库文件名,以此查找动态库文件,具体操作如下:

$ls

libtest.so main.c test.c test.o

$gcc -o main main.c -ltest -lm

$./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

程序出错,提示找不到动态库libtest.so。这是因为文件libtest.so并不在系统默认的库路径/lib或/usr/lib中,所以gcc在链接时无法找到该库,导致链接失败。解决的办法是将库复制到默认路径中,复制操作涉及权限问题,用户可用root身份执行。

#cp ./libtest.so /usr/lib/

上述命令中的“#”是root用户命令提示符。复制完毕后,即可正常编译用户程序。或者也可以使用-L选项指明动态库文件的路径,由于库在当前目录下,使用如下命令也可达到相同的效果:

$gcc -o main main.c -L . -ltest -lm

或者不使用-L选项,直接指定动态库全名也可达到同样的效果,命令如下:

$gcc -o main main.c ./libtest.so -lm

动态库与静态库的区别在于,如果删除了动态库,程序就无法执行。这一事实说明动态库文件中的函数在编译时并未链接到可执行文件中。

(3)库同名问题

在使用静态库和动态库编译目标程序的过程中,gcc命令的选项完全一样,那么,当静态库和动态库同名时,gcc命令会优先使用哪个库文件呢?测试命令如下:

#rm –f main *.o /usr/lib/libtest.so

#ls

main.c test.c

创建静态库文件libtest.a和动态库文件libtest.so。

$gcc -c test.c

$ar -src libtest.a test.o

$gcc -shared -fPIC test.o -o libtest.so

$ls

libtest.a libtest.so main main.c test.c test.o

通过ls命令可知,静态库文件libtest.a和动态库文件libtest.so都已在当前目录中生成。然后,使用gcc生成可执行文件main,命令如下:

$gcc -o main main.c -L . -ltest -lm

$./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

又出现了相同的错误,很容易判断出,当静态库和动态库同名时,gcc命令将使用动态库,也可以使用选项“-static”来指定编译时使用静态库。

$gcc –o main main.c -L . -static -ltest -lm

$./main

sin(x)=0.850904 This is a test

3.库链接

在使用动态库时,容易出现动态库路径问题,为了让可执行程序顺利找到动态库,有4种方法:

?把动态库复制到/usr/lib或/lib目录下。

?在LD_LIBRARY_PATH环境变量中加上库所在路径。例如,在/home/orange/demo/lib目录下存在动态库libhello.so,在shell提示符下使用如下命令完成路径添加:

$export

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/orange/demo/lib

?修改/etc/ld.so.conf文件,把库所在路径添加到该文件末尾,并执行ldconfig更新该文件。这样,新加入目录下的所有库文件都可见。

?不需要在/etc/ld.so.conf文件中添加动态库路径,而是先以root身份切换到动态库的目录下,再使用如下命令更新/etc/ld.so.conf。

#ldconfig 'pwd'/lib

上文中,ldconfig是管理动态库的命令,用来更新/etc/ld.so.conf文件,能够使动态库为系统所共享。该命令在/lib、/usr/lib等库文件标准路径以及配置文件/etc/ld.so.conf所列的目录下寻找动态库,而动态库的加载则由/lib/ld.so来完成。此后,系统会创建ld.so所需的链接和/etc/ld.so.cache文件,这是一个缓存文件,它保存了已排好序的动态库代码。

Linux下动态库采用了类似高速缓存的机制,库信息被保存在/etc/ld.so.cache中。程序链接时,首先从缓存文件中查找相关代码,如找不到,再去ld.so.conf指明的路径里查找。所以,当用户安装了一个新的动态库时,需要手工运行ldconfig命令。

4.函数符号库依赖

(1)函数符号

有时需要查看一个库文件中包含哪些函数,这里的库既可以是静态库也可是动态库。Linux提供了nm命令,可以打印出库中涉及的所有符号。对于每个符号,nm依次列出其地址偏移量(the symbol value)、类型(the symbol type)和名字(the symbol name)。其中the symbol value表示在内存中,符号在其所属段(如全局数据区、代码段等)中地址的偏移量。例如,查看EGUI动态库libconfig.so中的函数,部分显示如下:

[usr@localhost library]$ nm libconfig.so

000006a0 T config_exit

00000518 T config_init

00000522 T config_load

000019e4 b dtor_idx.5510

000004d0 t frame_dummy

    U free@@GLIBC_2.0

00001a00 B global_config

    U malloc@@GLIBC_2.0

    U puts@@GLIBC_2.0

nm列出的符号有很多,常见的有两种类型:

1)符号在库中被调用,但并没有在库中定义,即需要其他库支持,该符号类型用U表示。如上述结果的最后两行,函数malloc()和puts()是在标准C函数库glibc_2.0中定义,在当前库libconfig.so中被调用。

2)符号为库中定义的函数,用T表示。如前3行,在libconfig.so包含config_exit()、config_init()和config_load()函数的实现代码。

(2)库依赖

有时希望查看可执行文件所依赖的库文件,这些库可以是静态库,也可以是动态库,此时可以使用ldd命令。例如,查看前文生成可执行程序main的依赖文件:

$ ldd main

linux-gate.so.1 =>(0x00705000)

libtest.so => /home/orange/C/libtest.so (0x00578000)

lib.so.6 => /lib/libm.so.6(0x0052b000)

lib.so.6 => /lib/lib.c.so.6(0x00391000)

/lib/ld-linux.so.2(0x0036f000)

ldd命令结果指出,main程序执行过程中的各个依赖库包括libtest.so、libm.so.6和lib.c.so.6。同时还指出这些库在内存中的位置。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站