静态库和动态库编译方法和开发上的重要性
2016-04-16 10:47
423 查看
静态库和动态库的重要性
在整个产品研发过程中,主要涉及以下几点:==》需求:立项阶段
==》功能:立项阶段
==》架构:设计阶段
----> 关键技术:技术报告
----> 概要:系统架构
----> 接口:规范,稳定,完成,可扩展,高效率
----> 模块:功能单一,低耦合
----> 组件:松耦合
----> 部署:简单,方便,不影响业务
==》开发&测试:开发阶段
----> 静态库:链接
----> 动态库:部署
----> 模块开发:编码&调试
----> 测试用例:自动化测试
----> 集成联调:技术攻关,内部测试
==》版本发布:发布阶段
----> 测试回归:质量标准,形成alpha,beta,product标尺
随着系统开发的规模化和规范化,从系统架构和资源成本角度考虑,越来越多的代码被集成到模块,这样不仅提高了可维可测性,便于快速集成和部署。
模块和组件,在开发上主要依赖的以下几种方式来对设计进行管控:
==》代码工程结构
==》静态库
==》动态库
==》可执行文件
静态库和动态库编译方法介绍
说了这么多,主要是想说明下静态库和动态库在开发设计上的位置,以及重要性。接下去,就介绍下gcc静态库和动态库编译方法。示例代码源文件
为了说明这个问题,首先,创建A和B两个库源代码文件:so_a_test_A.c
#include <stdio.h> void test_printf_A(void) { printf("so_a_test print A!\n"); }
so_a_test_B.c
#include <stdio.h> void test_printf_B(void) { printf("so_a_test print B!\n"); }
然后,创建1和2两个主程序源代码文件:
main_1.c
#include <stdio.h> extern void test_printf_A(void); void main(void) { printf("hello, world!\n"); test_printf_A(); printf("hello, world done!\n"); }
main_2.c
#include <stdio.h> extern void test_printf_A(void); void main(void) { printf("hello, world!\n"); test_printf_A(); printf("hello, world done!\n"); }
静态库举例
静态库规划方式
1.代码工程结构==》so_a_test_A.c
==》so_a_test_B.c
==》main_1.c
==》main_2.c
2.静态库
==》libso_a_test.a
3.可执行文件
==》main_1,main_2
静态库编译执行
# gcc -c so_a_test_A.c # gcc -c so_a_test_B.c # ar cr libso_a_test.a so_a_test_A.o so_a_test_B.o # gcc -o main_1 main_1.c -L. -lso_a_test # gcc -o main_2 main_2.c -L. -lso_a_test
执行结果
# ./main_1 hello, world! so_a_test print A! so_a_test print B! hello, world done! # ./main_2 hello, world! so_a_test print A! hello, world done!
可执行文件依赖性分析
除了动态链接系统库就没有其他自定义的动态库。# ldd main_1 linux-vdso.so.1 => (0x00007fff65dfe000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa6b4148000) /lib64/ld-linux-x86-64.so.2 (0x00007fa6b451f000)
静态库链接分析
“+000000000040054c T test_printf_A”,so_a_test_B.c的代码没有被集成到main_2可执行文件里面,由于没有被main_2使用,被链接的时候剪裁掉了。# nm -s main_1 > main_1.txt # nm -s main_1 > main_1.txt # diff -uN main_1.txt main_2.txt --- main_1.txt 2016-04-15 18:44:42.990572340 -0700 +++ main_2.txt 2016-04-15 18:44:49.726571995 -0700 @@ -9,28 +9,27 @@ 0000000000600e28 d _DYNAMIC 0000000000601040 D _edata 0000000000601048 B _end -00000000004005f4 T _fini +00000000004005d4 T _fini 0000000000400500 t frame_dummy 0000000000600e10 t __frame_dummy_init_array_entry -00000000004007c0 r __FRAME_END__ +0000000000400768 r __FRAME_END__ 0000000000601000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 00000000004003e0 T _init 0000000000600e18 t __init_array_end 0000000000600e10 t __init_array_start -0000000000400600 R _IO_stdin_used +00000000004005e0 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000600e20 d __JCR_END__ 0000000000600e20 d __JCR_LIST__ w _Jv_RegisterClasses -00000000004005f0 T __libc_csu_fini -0000000000400580 T __libc_csu_init +00000000004005d0 T __libc_csu_fini +0000000000400560 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 000000000040052d T main U puts@@GLIBC_2.2.5 00000000004004a0 t register_tm_clones 0000000000400440 T _start -0000000000400551 T test_printf_A -0000000000400561 T test_printf_B +000000000040054c T test_printf_A 0000000000601040 D __TMC_END__
动态库举例
动态库规划方式
1.代码工程结构==》so_a_test_A.c
==》so_a_test_B.c
==》main_1.c
==》main_2.c
2.动态库
==》libso_a_test.so
3.可执行文件
==》main_1,main_2
动态库编译执行
# gcc -c -fPIC so_a_test_A.c # gcc -c -fPIC so_a_test_B.c # gcc -shared -fPIC -o libso_a_test.so so_a_test_A.o so_a_test_B.o # gcc -o main_1 main_1.c -L. -lso_a_test # gcc -o main_2 main_2.c -L. -lso_a_test
执行结果
# export LD_LIBRARY_PATH=./ # ./main_1 hello, world! so_a_test print A! so_a_test print B! hello, world done! # ./main_2 hello, world! so_a_test print A! hello, world done!
动态库链接分析
“- U test_printf_B”,so_a_test_B.c的代码没有被链接到main_2可执行文件里面,由于没有被main_2使用,被链接的时候符号链接剪裁掉了。# diff -uN main_1.txt main_2.txt --- main_1.txt 2016-04-15 18:53:12.266546288 -0700 +++ main_2.txt 2016-04-15 18:53:17.674546011 -0700 @@ -1,36 +1,35 @@ -0000000000601050 B __bss_start -0000000000601050 b completed.6973 -0000000000601040 D __data_start -0000000000601040 W data_start -0000000000400680 t deregister_tm_clones -00000000004006f0 t __do_global_dtors_aux +0000000000601048 B __bss_start +0000000000601048 b completed.6973 +0000000000601038 D __data_start +0000000000601038 W data_start +0000000000400630 t deregister_tm_clones +00000000004006a0 t __do_global_dtors_aux 0000000000600e08 t __do_global_dtors_aux_fini_array_entry -0000000000601048 D __dso_handle +0000000000601040 D __dso_handle 0000000000600e18 d _DYNAMIC -0000000000601050 D _edata -0000000000601058 B _end -00000000004007e4 T _fini -0000000000400710 t frame_dummy +0000000000601048 D _edata +0000000000601050 B _end +0000000000400784 T _fini +00000000004006c0 t frame_dummy 0000000000600e00 t __frame_dummy_init_array_entry -0000000000400940 r __FRAME_END__ +00000000004008e0 r __FRAME_END__ 0000000000601000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ -00000000004005c8 T _init +0000000000400588 T _init 0000000000600e08 t __init_array_end 0000000000600e00 t __init_array_start -00000000004007f0 R _IO_stdin_used +0000000000400790 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000600e10 d __JCR_END__ 0000000000600e10 d __JCR_LIST__ w _Jv_RegisterClasses -00000000004007e0 T __libc_csu_fini -0000000000400770 T __libc_csu_init +0000000000400780 T __libc_csu_fini +0000000000400710 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 -000000000040073d T main +00000000004006ed T main U puts@@GLIBC_2.2.5 -00000000004006b0 t register_tm_clones -0000000000400650 T _start +0000000000400660 t register_tm_clones +0000000000400600 T _start U test_printf_A - U test_printf_B -0000000000601050 D __TMC_END__ +0000000000601048 D __TMC_END__
可执行文件依赖性分析
除了动态链接系统库,就链接了自己开发的libso_a_test.so文件。# ldd main_1 linux-vdso.so.1 => (0x00007fffecad7000) libso_a_test.so => ./libso_a_test.so (0x00007f6dccbc2000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6dcc7ed000) /lib64/ld-linux-x86-64.so.2 (0x00007f6dccdc6000)
进一步分析libso_a_test.so文件,里面含有test_printf_A和test_printf_B代码段。
# nm -s libso_a_test.so 0000000000201038 B __bss_start 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 000000000000071c T _fini 00000000000006c0 t frame_dummy 0000000000200e00 t __frame_dummy_init_array_entry 00000000000007f0 r __FRAME_END__ 0000000000201000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 00000000000005a8 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000200e10 d __JCR_END__ 0000000000200e10 d __JCR_LIST__ w _Jv_RegisterClasses U puts@@GLIBC_2.2.5 0000000000000640 t register_tm_clones 00000000000006f5 T test_printf_A 0000000000000707 T test_printf_B 0000000000201038 d __TMC_END__
静态库和动态库规划对比点
可执行文件剪裁对比
==》静态链接采用的是"T",代码段嵌入==》动态链接采用的是"U",符号嵌入
==》静态链接可执行文件由于代码段植入,比动态链接可执行文件要大
==》main_1和main_2可执行文件从剪裁角度来说,都剪裁了“test_printf_B”,只不过一个剪裁的是代码,一个是符号
==》静态链接可执行文件依赖系统so库,而动态链接可执行文件依赖libso_a_test.so
静态、动态库文件对比
$ nm -s libso_a_test.a Archive index: test_printf_A in so_a_test_A.o test_printf_B in so_a_test_B.o so_a_test_A.o: U puts 0000000000000000 T test_printf_A so_a_test_B.o: U puts 0000000000000000 T test_printf_B
$ nm -s libso_a_test.so 0000000000201038 B __bss_start 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 000000000000071c T _fini 00000000000006c0 t frame_dummy 0000000000200e00 t __frame_dummy_init_array_entry 00000000000007f0 r __FRAME_END__ 0000000000201000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 00000000000005a8 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000200e10 d __JCR_END__ 0000000000200e10 d __JCR_LIST__ w _Jv_RegisterClasses U puts@@GLIBC_2.2.5 0000000000000640 t register_tm_clones 00000000000006f5 T test_printf_A 0000000000000707 T test_printf_B 0000000000201038 d __TMC_END__
$ ldd libso_a_test.so linux-vdso.so.1 => (0x00007fffb13fe000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa85b418000) /lib64/ld-linux-x86-64.so.2 (0x00007fa85b9f1000)
$ ldd libso_a_test.a not a dynamic executable
==》静态库给出了函数工程代码源文件出处,动态库没有
==》动态库有系统支持代码,静态库没有
==》动态库有系统支持代码,所以文件较静态库大
==》动态库有默认的系统初始化代码和去初始化代码,可以引入组件支持和对象模型机制
代码工程结构对静态库的剪裁影响
将so_a_test_A.c和so_a_test_B.c代码整合到一个文件so_a_test.c#include <stdio.h> void test_printf_A(void) { printf("so_a_test print A!\n"); }
void test_printf_B(void)
{
printf("so_a_test print B!\n");
}
编译,执行,分析,可以看出,工程整合后,静态链接的剪裁效果消失了,不管main_1,main_2是否都用到该函数,代码段都将被链接。这也是为什么需要模块化切分,可以减少可执行文件的大小。
# gcc -c so_a_test.c # ar cr libso_a_test.a so_a_test.o # nm -s libso_a_test.a Archive index: test_printf_A in so_a_test.o test_printf_B in so_a_test.o so_a_test.o: U puts 0000000000000000 T test_printf_A 0000000000000010 T test_printf_B # gcc -o main_1 main_1.c -L. -lso_a_test # gcc -o main_2 main_2.c -L. -lso_a_test # ./main_1 hello, world! so_a_test print A! so_a_test print B! hello, world done! # ./main_2 hello, world! so_a_test print A! hello, world done! # nm -s main_1 > main_1.txt # nm -s main_2 > main_2.txt # diff -uN main_1.txt main_2.txt --- main_1.txt 2016-04-15 19:37:50.206409296 -0700 +++ main_2.txt 2016-04-15 19:37:54.834409059 -0700 @@ -9,28 +9,28 @@ 0000000000600e28 d _DYNAMIC 0000000000601040 D _edata 0000000000601048 B _end -00000000004005f4 T _fini +00000000004005e4 T _fini 0000000000400500 t frame_dummy 0000000000600e10 t __frame_dummy_init_array_entry -00000000004007c0 r __FRAME_END__ +00000000004007b0 r __FRAME_END__ 0000000000601000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 00000000004003e0 T _init 0000000000600e18 t __init_array_end 0000000000600e10 t __init_array_start -0000000000400600 R _IO_stdin_used +00000000004005f0 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000600e20 d __JCR_END__ 0000000000600e20 d __JCR_LIST__ w _Jv_RegisterClasses -00000000004005f0 T __libc_csu_fini -0000000000400580 T __libc_csu_init +00000000004005e0 T __libc_csu_fini +0000000000400570 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 000000000040052d T main U puts@@GLIBC_2.2.5 00000000004004a0 t register_tm_clones 0000000000400440 T _start -0000000000400551 T test_printf_A -0000000000400561 T test_printf_B +000000000040054c T test_printf_A +000000000040055c T test_printf_B 0000000000601040 D __TMC_END__
补充知识点:nm常见的符号类型
A 该符号的值在今后的链接中将不再改变;B 该符号放在BSS段中,通常是那些未初始化的全局变量;
D 该符号放在普通的数据段中,通常是那些已经初始化的全局变量;
T 该符号放在代码段中,通常是那些全局非静态函数;
U 该符号未定义过,需要自其他对象文件中链接进来;
W 未明确指定的弱链接符号;同链接的其他对象文件中有它的定义就用上,否则就用一个系统特别指定的默认值。
参考资料
【1】gcc使用指南相关文章推荐
- FZU 2144 Shooting Game (贪心区域划分)
- Java中字符串String Switch的实现原理
- 网络直播电视之M3U8解析篇 (下)
- 合并两个排序的链表
- IOS学习之——ViewController之间正向传值
- 字符串计数(动态规划)
- hibernate对连接池的支持
- Reflector_8.3.0.93_安装文件及破解工具
- 20145315 《Java程序设计》第七周学习总结
- iOS 开发 Pch 文件的正确使用
- LeetCode 175 -Combine Two Tables ( MYSQL )
- 用javaagent来对java字节码进行变换
- nyoj 183 赚钱了 bellmanford
- Partitioning Game (SG函数)
- 在Android开发中使用Ant 一:环境的搭建及入门
- Apriori算法代码及实例
- CAP原理
- 从公网访问openwrt上用mjpeg-streamer搭建的视频监控
- 部分函数依赖 && 完全函数依赖
- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path