在linux系统中,可将多个目标文件打包成库文件,以便在编程时随时调用,而不必重新编写或定义,这种包称为库函数。库文件是一些预先编译好
的函数的集合,那些函数都是按照可再使用的原则编写的。它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数
(curses库)等。
1.基础知识:
linux下GCC在编译程序时要经历预处理,编译,汇编和连接四个阶段。
预处理阶段,主要处理#include和#define,它把#include包含进来的.h 文件插入到#include所在的位置,生成.i文件;
编译阶段,是最重要的阶段,在这个阶段GCC首先检查语法然后把文件转换成汇编程序,生成.s文件;上面这两步的输出文件都是文本文件,我们可以用诸如cat的文本处理等命令阅读这些输出文件;
汇编阶段,把*.s文件翻译成二进制机器指令文件*.o,需要反汇编工具如GDB的帮助才能读懂它;
连接阶段,gcc在这个阶段把所有的*.o文件连接成一个可执行文件,库文件的连接也在这步完成。
2. 标准库与非标准库
库函数可分为标准与非标准(自定义)库两大类。
(1)标准库文件是公用的,系统中的任何用户都可以利用这些库函数,一般保存在/lib或者/usr/lib目录里,并以头文件的方式提供包含调用。编译时要告诉C语言编译器(更确切地说是链接程序)应去查找哪些库文件,默认情况下,它只会查找标准库文件。
标准库在使用时,gcc等编译程序能够自动连接,所以在只需要包含其定义的头文件即可,如libc.a。libc.a为标准C函数库,它包含了诸如内存管理或者输入输出操作的基本函数。
(2)非标准库在连接时,必须加上-lname(name为去掉lib和尾部的.a或.so后的库名)参数;非标准库可以放在任意目录中,一般放于当前目录中,但当放置在非系统默认搜索路径中时,需要用-Ldir(dir为路径名称)指定搜索路径。
例如,数学函数并不是C标准库的组成部分,他们是由数学库/usr/lib/libm.a所定义的,因而在使用该库中的数学函数时,除了
用#include将头文件/usr/include/math.h加入到程序文件中,还要明确的用gcc的-lm选项来连
接这个库(数学库libm.a放置在系统默认的库搜索路径/usr/lib中,因而不需要-Ldir参数)。
3. 静态库与共享库
函数库一般分为静态和共享(也称动态库)两种格式,库文件必须遵守一定的命名规则,库文件的名字永远以lib打头,随后是说明函数库情况的部分(比如用c
表示这是一个
C语言库,而m表示这是一个数学运算库等)。文件名的最后部分以一个句点(.)开始,然后给出这个库文件的类型,.a为静态库文件,.so和.sa为共享
型库文件。无论动态库还是静态库都需要用.o文件来生成。
一般 Linux 系统把 /lib 和 /usr/lib
两个目录作为默认的库(动态库或静态库)搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需
要将库的位置添加到库的搜索路径之中(如修改环境变量),也可以在程序连接时,通过-L参数来指定。
但由于在运行时,程序还需要连接动态库,因而,对于动态库,最好还是将库的位置添加到库的搜索路径之中或是把所用的库拷贝到系统默认的库搜索目录中。静态库则无此限制。
(1)静态库
静态库也叫做档案(archive),文件名按惯例都以.a结尾,比如C语言标准库/usr/lib/libc.a,X11库/usr/X11R6
/lib/libX11.a等。它所包含的成员是若干.o文件,静态库中的各个成员(.o文件)没有特殊的存在格式,仅仅是一个.o文件的集合。在连接
时,静态库的文件代码会被拷贝到可执行文件中,所以静态连接的可执行文件一般比较大一些。
在Linux(Unix)中使用工具“ar”对静态库进行维护管理,创建静态库用ar命令,在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
# ar -rc libmyhello.a hello.o
静态库的缺点是,如果我们在同一时间运行多个程序而它们又都使用着来自同一个函数库里的函数时,内存里就会有许多份同一函数的备份,在程序文件本身也有许多份同样的备份。这会消耗大量宝贵的内存和硬盘空间。
(2)共享库
也称动态库,许多UNIX系统支持共享库,它同时克服了静态库在内存和硬盘方面的无谓消耗。链接时,动态库的代码不会被加入可执行文件中,而是在程序被执行的时候加载。
共享库的默认存放位置和静态库是一样的,但有着不同的文件后缀。在一个典型的 Linux系统上,C语言标准库的共享版本是/usr/lib/libc.so N,其中的N是主版本号。当静态库和动态库同名时,gcc命令将优先使用动态库。
可用gcc来创建动态库,在系统提示符下键入以下命令得到动态库文件libmyhello.so。
# gcc -shared -fPCI -o libmyhello.so hello.o
4. 对比
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要动态库存在。
假如程式是在编译时加载库文档的,就是使用了静态库。假如是在运行时加载目标代码,就成为动态库。换句话说,假如是使用静态库,则静态库代码在编译时就拷
贝到了程式的代码段,程式的体积会膨胀。假如使用动态库,则程式中只保留库文档的名字和函数名,在运行时去查找库文档和函数体,程式的体积基本变化不大。
静态库的原则是“以空间换时间”,增加程式体积,减少运行时间;动态库则是“以时间换空间”,增加了运行时间,但减少了程式本身的体积。
5. 总结
标准静态库或共享库,路径和库名都不需显示指定;
非标准静态库,必须显示指定库名,不在默认路径中时需指定路径;
非标准共享库,编译时:必须指定库名,不在默认路径中需指定路径名,
运行时,不在默认路径中时需要将库位置添加到库搜索路径中或拷贝库到默认路径中。
6. gcc相关命令
使用要点:
⑴在gcc 的-I参数后加上库头文件的路径;
⑵在gcc 的-L参数后加上库文件所在目录;
⑶在gcc 的-l参数后加上库文件名,但是要去掉lib和.a扩展名,比如库文件名是libtest.a 那么参数就是-ltest。
静态库的使用
创建静态库的步骤如下
(1) 在一个头文件mylib.h中声明静态库所导出的函数
(2) 在一个源文件mylib.c中实现静态库所导出的函数
(3) 编译源文件,生成目标代码,gcc -o mylib.o -c mylib.c
(4) 将目标文件mylib.o加入到某个静态库中,并将静态库拷贝到系统默认库目录下。
ar rcs libmylib.a mylib.o
mylib.h中存放了静态库提供给用户使用的函数声明,mylib.c实现了mylib.h中声明的函数
头文件mylib.h
#ifndef _MYLIB_H_ #define _MYLIB_H_
void welcome(); void outstring(const char *str);
#endif /* _MYLIB_H_ */
|
对应头文件mylib.h的源文件mylib.c
#include "mylib.h" #include <stdio.h> void welcome() { printf("Welcome to libmylib\n"); } void outstring(const char *str) { if(str!=NULL) printf("%s\n",str); }
|
编译mylib.c生成目标文件 gcc -o mylib.o -c mylib.c
将目标文件加入到静态库中 ar rcs libmylib.a mylib.o
将静态库拷贝到系统默认库目录(/lib /usr/lib)
cp libmylib.a /usr/lib/libmylib.a
以下为调用自定义库的测试程序 test.c
#include "mylib.h" #include <stdio.h> int main() { printf("create and use library:\n"); welcome(); outstring("It's successful"); }
|
编译命令: gcc -o test test.c -lmylib
libmylib.a 和test.c在同一目录下,可以 gcc -o test test.c libmylib.a
动态库的使用
在linux环境下,可以很方便的创建和使用动态链接库。只要在编译函数库源程序时候加上 -shared
选项即可。这样生成可执行程序就为动态链接库。从某种意义上讲,动态链接库是以一种可执行程序。按一般规则,动态链接库以.so后缀。下面命令把
mylib.c程序创建成一个动态链接库
gcc -fPIC mylib.o -c mylib.c
gcc -shared -o libttt.so mylib.o
也可以直接用 gcc -fPIC -shared -o libttt.so mylib.c
动态链接库创建后就可以使用了。linux有两种方式调用动态链接库的函数,一种是像使用静态链接库一样,通过gcc命令,格式如下 gcc
-o test test.c ./libttt.so或者cp libttt.so /usr/lib/libttt.so gcc -o test
test.c /usr/lib/libttt.so
注意:引用动态链接库时,必须包含路径,如果只是使用libmylib.so,则必须确保这个库的目录包含在PATH环境变量中
另一种方法是通过使用调用系统函数来使用动态链接库,相关函数如表
函数 |
说明 |
void* dlopen(const char *filename,int flag) |
用于打开指定名字的动态链接库,并返回一句柄 |
void* dlsym(void *handle,char *symbol) |
根据动态链接库句柄和函数名,返回函数名对应的地址 |
int dlclose(void *handle) |
关闭动态链接库 |
const char *dlerror(void) |
动态链接库中函数执行失败时,dlerror返回出错信息,执行成功返回NULL |
dlopen函数的参数flag 可取的值有:RTLD_LAZY RTLD_NOW RTLD_GLOBAL含义如下
RTLD_LAZY: 在dlopen()返回前,对于动态链接库中存在的未定义的变量(如外部变量extern,也可以是函数)不解析执行,也就是不解析这个变量的地址
RTLD_NOW 解析出每个未定义变量的地址,如果解析不出来,dlopen放回NULL,错误为 undefined symbbol:xxx.....
RTLD_GLOBAL 使库中解析出来的变量在随后的其他链接库中也可以使用,即全局有效
testso.c
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> int main(void) { void *handle; char *error; void (*welcome)();
if((handle=dlopen("./libttt.so",RTLD_LAZY))==NULL) { printf("dlopen error\n"); exit(1); }
welcome=dlsym(handle,"welcome"); if((error=dlerror())!=NULL) { printf("dlsym error\n"); exit(1); }
welcome(); dlclose(handle); exit(0); }
|
# gcc -o testso testso.c -ldl
zz http://blogold.chinaunix.net/u3/103292/showart_2390408.html
阅读(1790) | 评论(0) | 转发(0) |