一、库的说明
库是用于提供特定功能的函数接口的集合,以文件形式存在。在linux系统中,分动态链接库和静态链接库两种,简称动态库和静态库。其中,静态库文件以.a为后缀为名,动态库文件以.so为后缀名。
二、动态库和静态库的区别
- 静态库在程序编译时链接到目标程序中,在程序运行时不需要静态库文件。由于静态库中的文件都编译到了整个程序中,导致目标程序体积变大。
- 动态库在程序编译时不会被链接到目标程序中,可执行文件体积较小。程序在运行的时候加载动态库,因此需要保证程序运行所在的环境中有相应的动态库文件
三、静态库的生成和使用
3.1 静态库的生成
源码:
catlog.c:
#include <stdio.h>
int cat_log_show_str(char *info)
{
printf("%s: %s\n", __FUNCTION__, info);
return 0;
}
int cat_log_show_digit(int digit)
{
printf("%s: %d\n", __FUNCTION__, digit);
return 0;
}
catlog.h:
#ifndef __CAT_LOG_H__
#define __CAT_LOG_H__
int cat_log_show_str(char *info);
int cat_log_show_digit(int digit);
#endif
生成库命令:
gcc -c catlog.c
ar cr libcatlog.a catlog.o
ranlib libcatlog.a
3.2 静态库查看
查看库文件中由哪些文件组成:
$ ar -t libcatlog.a
catlog.o
列出目标文件中的符号:
$ nm -s libcatlog.a
Archive index:
cat_log_show_str in catlog.o
cat_log_show_digit in catlog.o
catlog.o:
000000000000002e T cat_log_show_digit
0000000000000000 T cat_log_show_str
0000000000000010 r __FUNCTION__.2179
0000000000000030 r __FUNCTION__.2183
U printf
3.3 静态库的使用
使用静态库中的函数时,需要包含静态库对应的头文件,并且在编译时指定通过-l链接的库名libcatlog.a
源码分析:
catlog.c:
#include <stdio.h>
int cat_log_show_str(char *info)
{
printf("%s: %s\n", __FUNCTION__, info);
return 0;
}
int cat_log_show_digit(int digit)
{
printf("%s: %d\n", __FUNCTION__, digit);
return 0;
}
catlog.h:
#ifndef __CAT_LOG_H__
#define __CAT_LOG_H__
int cat_log_show_str(char *info);
int cat_log_show_digit(int digit);
#endif
main.c:
#include <stdio.h>
#include "catlog.h"
int main(int argc, char *argv[])
{
cat_log_show_str("hello world");
cat_log_show_digit(5);
return 0;
}
Makefile:
all: lib bin
lib:
gcc -c catlog.c
ar cr libcatlog.a catlog.o
ranlib libcatlog.a
bin:
gcc main.c -L. -lcatlog
clean:
rm *.o
rm *.a
rm a.out
测试:
$ ./a.out
cat_log_show_str: hello world
cat_log_show_digit: 5
四、动态库的生成和使用
4.1 动态库的生成
源码:
catlog.c:
#include <stdio.h>
int cat_log_show_str(char *info)
{
printf("%s: %s\n", __FUNCTION__, info);
return 0;
}
int cat_log_show_digit(int digit)
{
printf("%s: %d\n", __FUNCTION__, digit);
return 0;
}
catlog.h:
#ifndef __CAT_LOG_H__
#define __CAT_LOG_H__
int cat_log_show_str(char *info);
int cat_log_show_digit(int digit);
#endif
动态库的编译:
$ gcc -fPIC -c catlog.c
$ gcc -shared -o libcatlog.so catlog.o
4.2 动态库的查看
查看符号:
$ nm -s libcatlog.so
0000000000201038 B __bss_start
0000000000000727 T cat_log_show_digit
00000000000006f5 T cat_log_show_str
0000000000201038 b completed.6973
w __cxa_finalize@@GLIBC_2.2.5
0000000000000610 t deregister_tm_clones
0000000000000680 t __do_global_dtors_aux
0000000000200e08 t __do_global_dtors_aux_fini_array_entry
0000000000201030 d __dso_handle
0000000000200e18 d _DYNAMIC
0000000000201038 D _edata
0000000000201040 B _end
0000000000000758 T _fini
00000000000006c0 t frame_dummy
0000000000200e00 t __frame_dummy_init_array_entry
0000000000000858 r __FRAME_END__
0000000000000780 r __FUNCTION__.2179
00000000000007a0 r __FUNCTION__.2183
0000000000201000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000000005b0 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000200e10 d __JCR_END__
0000000000200e10 d __JCR_LIST__
w _Jv_RegisterClasses
U printf@@GLIBC_2.2.5
0000000000000640 t register_tm_clones
0000000000201038 d __TMC_END__
4.3 动态库的使用
catlog.c:
#include <stdio.h>
int cat_log_show_str(char *info)
{
printf("%s: %s\n", __FUNCTION__, info);
return 0;
}
int cat_log_show_digit(int digit)
{
printf("%s: %d\n", __FUNCTION__, digit);
return 0;
}
catlog.h:
#ifndef __CAT_LOG_H__
#define __CAT_LOG_H__
int cat_log_show_str(char *info);
int cat_log_show_digit(int digit);
#endif
main.c:
#include <stdio.h>
#include "catlog.h"
int main(int argc, char *argv[])
{
cat_log_show_str("hello world");
cat_log_show_digit(5);
return 0;
}
Makefile:
all: lib bin
lib:
gcc -fPIC -c catlog.c
gcc -shared -o libcatlog.so catlog.o
bin:
gcc main.c -L. -lcatlog
clean:
rm *.o
rm *.so
rm a.out
查看程序链接的动态库:
$ ldd a.out
linux-vdso.so.1 => (0x00007fff0a49d000)
**libcatlog.so** => /usr/lib/**libcatlog.so** (0x00007f85510f7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8550d32000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8551316000)
测试:
$ ./a.out
./a.out: error while loading shared libraries:
libcatlog.so: cannot open shared object file:
No such file or directory
出现上面的错误原因是程序默认在系统目录/lib/和/usr/lib/目录下搜索动态库,因此在实际应用中都会将动态库安装(拷贝)到/lib/或/usr/lib/目录下
$ cp libcatlog.so /usr/lib/
再次运行,程序正常。有时尽管已经将动态库安装(拷贝)到/lib/或/usr/lib/目录下,但依然提示上述错误,则需要执行ldconfig命令刷新一下
测试:
$ ./a.out
cat_log_show_str: hello world
cat_log_show_digit: 5
除了将动态库安装(拷贝)到/lib/或/usr/lib/目录下外,还可以通过环境变量LD_LIBRARY_PATH指定额外的动态库搜索路径,当程序在默认路径/lib/和/usr/lib/下找不到动态库时,会在LD_LIBRARY_PATH指定的路径下搜索动态库,这个方法,对于临时测试是很有用的。
五、动态库和静态库的优先级
在一般的开源库中,会同时生成动态库和静态库。如果路径下同时存在动态库和静态库时,程序在链接时会优先选择动态库,测试如下:
catlog.c:
#include <stdio.h>
int cat_log_show_str(char *info)
{
printf("%s: %s\n", __FUNCTION__, info);
return 0;
}
int cat_log_show_digit(int digit)
{
printf("%s: %d\n", __FUNCTION__, digit);
return 0;
}
catlog.h:
#ifndef __CAT_LOG_H__
#define __CAT_LOG_H__
int cat_log_show_str(char *info);
int cat_log_show_digit(int digit);
#endif
main.c:
#include <stdio.h>
#include "catlog.h"
int main(int argc, char *argv[])
{
cat_log_show_str("hello world");
cat_log_show_digit(5);
return 0;
}
Makefile:
all: lib-static lib-dynamic bin
lib-dynamic:
gcc -fPIC -c catlog.c
gcc -shared -o libcatlog.so catlog.o
lib-static:
gcc -c catlog.c
ar cr libcatlog.a catlog.o
ranlib libcatlog.a
bin:
gcc main.c -L. -lcatlog
clean:
rm *.o
rm *.so
rm a.out
编译后,生成动态库和静态库,使用ldd查看a.out的链接库
$ ldd a.out
linux-vdso.so.1 => (0x00007fff2ab5c000)
**libcatlog.so** => /usr/lib/libcatlog.so (0x00007fd931b6e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd9317a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd931d8d000)
修改Makefile如下,重新编译,只生成静态库,这时可以看到程序没有链接libcatlog.so
$ ldd a.out
linux-vdso.so.1 => (0x00007fff5b7fc000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f65327c9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6532bab000)
六、动态库和静态库的特点及应用
- 静态库和程序编译在一起,部署时不需要静态库文件
- 动态库在程序运行时加载,因此,部署程序时需要同时安装动态库
- 使用静态库的程序由于包含了静态库,程序会比较大,而使用动态库的程序会比较小
- 无论是动态库还是静态库,都会有对应的头文件供用户编程使用
- 向第三方提供功能模块或SDK时,一般不提供源码,而是使用动态库或静态库的形式,这样可以避免核心技术泄密