转载自:http://www.yuanma.org/data/2007/1007/article_2865.htm
说明:本文例子代码(包括静态库、共享库、动态加载共享库)下载,下载地址:
http://pickup.mofile.com/0760747733420493
一、lib类型:
与windows下静态库(.lib)和动态库(.dll)一样,linux同样存在静态库(static library 文件后缀为.a)和共享库(shared library 文件后缀为.so),在/usr/lib目录下同时存在一个库的静态版本和动态版本。
"An archive (or static library) is simply a collection of object files stored as a single file.
When you provide an archive to the linker, the linker searches the archive for the object files
it needs, extracts them, and links them into your program much as if you had provided those
object files directly."
"A shared library is similar to a archive in that it is a grouping of object files. However,
there are many important differences.The most fundamental difference is that when a shared library is
linked into a program, the final executable does not actually contain the code that is
present in the shared library. Instead, the executable merely contains a reference to the
shared library."
"the object files that compose the shared library are combined into a
single object file so that a program that links against a shared library always includes all
of the code in the library, rather than just those portions that are needed."
以上引自《Advanced Linux Programming》
由此可知,静态库和共享库都是一个obj文件的集合,但静态链接后,执行程序中存在自己所需obj的一份拷贝,而动态链接后,执行程序仅仅是包含对共享库的一个引用。共享库相当于一个由多个obj文件组合而成的obj文件,在链接后其所有代码被加载,不管需要的还是不需要的。
似乎可以得出一个结论:
静态链接后的程序比动态链接的所用存储空间大,因为执行程序中包含了库中代码拷贝;
而动态链接的程序比静态链接的所用的运行空间大,因为它将不需要的代码也加载到运行空间。
二、lib编译:
静态库的编译,实际上是一个将.o文件打包的过程。
ar -rc libfunc.a func1.o func2.o # 将f1.o、f2.o编译成静态库libfunc.a
动态库的编译,使用gcc -fPIC -shared编译选项。
gcc -fPIC -shared -o libfunc.so func1.o func2.o # 将f1.o、f2.o编译成动态库libfunc.so
三、lib调用:
静态库的调用比较简单,跟标准库函数调用一样
一个例子:main.c调用./lib目录下libfunc.a库,该库头文件在./inc目录下,编译:
gcc -o test main.c -I./inc -L./lib -lfunc
共享库的调用需要注意库文件放置位置,如果该库文件不在/lib、/usr/lib下,则需要设置LD_LIBRARY_PATH变量。
一个例子:main.c调用./lib目录下libfunc.so库,该库头文件在./inc目录下,如果使用编译:
gcc -o test main.c -I./inc -L./lib -lfunc
./test # 运行错误:error while loading shared libraries: libfunc.so
这是因为动态链接时程序只是存放共享库的名称而不是绝对路径,所以运行时我们需要先设置该库所处位置:
export LD_LIBRARY_PATH=./lib
./test # 运行成功
动态装载共享库:在只有共享库而没有库的头文件,或者你想在运行时动态加载、卸载库时,linux的dl库函数:dlopen、dlclose、dlsym帮你办到,其相当于windows下LoadLibrary、FreeLibrary、GetProcAddress函数
函数原型:
void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
一个例子:main.c动态装载./lib目录下libfunc.so库,库中有一个函数void print_str(const char*);
/*加载库*/
void *handle = dlopen("libfunc.so", RTLD_LAZY);
/*获得函数的入口*/
void (*pt_str)(const char*);
pt_str = dlsym(handle, "print_str");
/*调用函数*/
pt_str("hello world.");
/*卸载库*/
dlclose(handle);
四、相关说明:
1、共享库特别适合多个程序共享代码,升级程序部分功能模块,实现程序“插件”功能的情况;
而静态库是一劳永逸,编译后不需要带一堆库文件跑,而且不管放置到哪里都可正常运行。
2、当搜索的库文件目录下同时存在该库的静态版本和共享版本时,链接器优先使用共享版本.so,此时你可以使用-static链接选项指定链接静态版本.a。
3、动态库可以导出两个特殊的函数:_init和_fini,前者在动态库被加载后调用,后者在动态库被卸载前调用,
我们可以使用这两个函数做些特别的工作。需要注意的是:在定义这两个函数后编译时,需要使用
-nostartfiles选项,否则编译器报重复定义错误。
4、ldd命令用来查看程序所依赖的共享库,同时也方便我们判断共享库是否被找到;
nm命令查看obj文件(.so也是一个obj)中的标识(函数、变量)。