两个要知道的基本知识html
Linux 应用程序由于 Linux 版本的众多与各自独立性,在工程制做与使用中必须熟练掌握以下两点才能有效地工做和理想地运行。linux
为了演示三种不一样的标准库连接方式对最终应用程序产生的区别, 这里用了一个经典的示例应用程序 HelloWorld 作演示,见 清单 1 HelloWorld。 整个工程能够在文章末尾下载。c++
#include <stdio.h> #include <iostream> using std::cout; using std::endl; int main(int argc, char* argv[]) { printf("HelloWorld!(Printed by printf)\n"); cout<<"HelloWorld!(Printed by cout)"<<endl; return 0; } |
三种标准库连接方式的选项及区别见 表 1this
标准库链接方式 | 示例链接选项 | 优势 | 缺点 |
---|---|---|---|
全静态 | -static -pthread -lrt -ldl | 不会发生应用程序在 不一样 Linux 版本下的标准库不兼容问题。 | 生成的文件比较大, 应用程序功能受限(不能调用动态库等) |
全动态 | -pthread -lrt -ldl | 生成文件是三者中最小的 | 比较容易发生应用程序在 不一样 Linux 版本下标准库依赖不兼容问题。 |
半静态 (libgcc,libstdc++) | -static-libgcc -L. -pthread -lrt -ldl | 灵活度大,可以针对不一样的标准库采起不一样的连接策略, 从而避免不兼容问题发生。 结合了全静态与全动态两种连接方式的优势。 |
比较难识别哪些库容易发生不兼容问题, 目前只有依靠经验积累。 某些功能会因选择的标准库版本而丧失。 |
上述三种标准库连接方式中,比较特殊的是 半静态连接方式,主要在于其还须要在连接前增长额外的一个步骤:
ln -s `g++ -print-file-name=libstdc++.a`,做用是将 libstdc++.a(libstdc++ 的静态库)符号连接到本地工程连接目录。
-print-file-name 在 gcc 中的解释以下:
-print-file-name=<lib> Display the full path to library <lib>spa
为了区分三种不一样的标准库连接方式对最终生成的可执行文件的影响,本文从两个不一样的维度进行分析比较:.net
维度一:最终生成的可执行文件对标准库的依赖方式(使用 ldd 命令进行分析)code
ldd 简介:该命令用于打印出某个应用程序或者动态库所依赖的动态库
涉及语法:ldd [OPTION]... FILE...
其余详细说明请参阅 man 说明。orm
三种标准库连接方式最终产生的应用程序的可执行文件对于标准库的依赖方式具体差别见 图 1、图 2、图 3所示:xml
经过上述三图,能够清楚的看到,当用 全静态标准库的连接方式时,所生成的可执行文件最终不依赖任何的动态标准库,
而 全动态标准库的连接方式会致使最终应用程序可执行文件依赖于全部用到的标准动态库。
区别于上述两种方式的 半静态连接方式则有针对性的将 libgcc 和 libstdc++ 两个标准库非动态连接。
(对比 图 2与 图 3,可见在 图 3中这两个标准库的动态依赖不见了)
从实际应用当中发现,最理想的标准库连接方式就是半静态连接,一般会选择将 libgcc 与 libstdc++ 这两个标准库静态连接,
从而避免应用程序在不一样 Linux 版本间标准库依赖不兼容的问题发生。
维度二 : 最终生成的可执行文件大小(使用 size 命令进行分析)
size 简介:该命令用于显示出可执行文件的大小
涉及语法:size objfile...
其余详细说明请参阅 man 说明。
三种标准库连接方式最终产生的应用程序的可执行文件的大小具体差别见 图 4、图 5、图 6所示:
经过上述三图能够看出,最终可执行文件的大小随最终所依赖的标准动态库的数量增长而减少。
从实际应用当中发现,最理想的是 半静态连接方式,由于该方式可以在避免应用程序于
不一样 Linux 版本间标准库依赖不兼容的问题发生的同时,使最终生成的可执行文件大小最小化。
-llibrary
-l library:指定所须要的额外库
-Ldir:指定库搜索路径
-static:静态连接全部库
-static-libgcc:静态连接 gcc 库
-static-libstdc++:静态连接 c++ 库
关于上述命令的详细说明,请参阅 GCC 技术手册
ar 简介:处理建立、修改、提取静态库的操做
涉及选项:
t - 显示静态库的内容
r[ab][f][u] - 更新或增长新文件到静态库中
[s] - 建立文档索引
ar -M [<mri-script] - 使用 ar 脚本处理
其余详细说明请参阅 man 说明。
假设现有如 图 7所示两个库文件
从 图 7中能够得知,CdtLog.a 只包含 CdtLog.o 一个对象文件 , 而 xml.a 包含 TXmlParser.o 和 xmlparser.o 两个对象文件
现将 CdtLog.o 提取出来,而后经过 图 8方式建立一个新的静态库 demo.a,能够看出,demo.a 包含的是 CdtLog.o 以及 xml.a,
而不是咱们所预期的 CdtLog.o,TXmlParser.o 和 xmlparser.o。这正是区别于 Windows 下静态库的制做。
这样的 demo.a 当被连接入某个工程时,全部在 TXmlParser.o 和 xmlparser.o 定义的符号都不会被发现,从而会致使连接错误,
提示没法找到对应的符号。显然,经过图 8 方式建立 Linux 静态库是不正确的。
正确的方式有两种:
显然,方式 1 是比较麻烦的,由于涉及到太多的文件处理,可能还要经过不断建立临时目录用于保存中间文件。
推荐使用如 清单 2 createlib.sh所示的 ar 脚本方式进行建立:
rm demo.a rm ar.mac echo CREATE demo.a > ar.mac echo SAVE >> ar.mac echo END >> ar.mac ar -M < ar.mac ar -q demo.a CdtLog.o echo OPEN demo.a > ar.mac echo ADDLIB xml.a >> ar.mac echo SAVE >> ar.mac echo END >> ar.mac ar -M < ar.mac rm ar.mac |
若是想在 Linux makefile 中使用 ar 脚本方式进行静态库的建立,能够编写如 清单 3 BUILD_LIBRARY所示的代码:
define BUILD_LIBRARY $(if $(wildcard $@),@$(RM) $@) $(if $(wildcard ar.mac),@$(RM) ar.mac) $(if $(filter %.a, $^), @echo CREATE $@ > ar.mac @echo SAVE >> ar.mac @echo END >> ar.mac @$(AR) -M < ar.mac ) $(if $(filter %.o,$^),@$(AR) -q $@ $(filter %.o, $^)) $(if $(filter %.a, $^), @echo OPEN $@ > ar.mac $(foreach LIB, $(filter %.a, $^), @echo ADDLIB $(LIB) >> ar.mac ) @echo SAVE >> ar.mac @echo END >> ar.mac @$(AR) -M < ar.mac @$(RM) ar.mac ) endef $(TargetDir)/$(TargetFileName):$(OBJS) $(BUILD_LIBRARY) |
经过 图 9,咱们能够看到,用这种方式产生的 demo.a 才是咱们想要的结果。
正如 GCC 手册中提到的那样:
It makes a difference where in the command you write this option; the linker
searches and processes libraries and object files in the order they are specified.
Thus, ‘ foo.o -lz bar.o ’ searches library ‘ z ’ after file ‘ foo.o ’ but before
‘ bar.o ’ . If ‘ bar.o ’ refers to functions in ‘ z ’ , those functions may not be loaded.
为了解决这种库连接顺序问题,咱们须要增长一些连接选项 :
$(CXX) $(LINKFLAGS) $(OBJS) -Xlinker "-(" $(LIBS) -Xlinker "-)" -o $@
经过将全部须要被连接的静态库放入 -Xlinker "-(" 与 -Xlinker "-)" 之间,能够是 g++ 连接过程当中, 自动循环连接全部静态库,从而解决了本来的连接顺序问题。
-Xlinker option
Pass option as an option to the linker. You can use this to supply system-specific
linker options which GCC does not know how to recognize.
本文介绍了 Linux 下三种标准库连接的方式及各自利弊,同时还介绍了 Linux 下静态库的制做及使用方法,相信可以给 大多数须要部署 Linux 应用程序和编写 Linux Makefile 的工程师提供有用的帮助。