linux 库全局变量_跟我学c++中级篇——Linux下的动态库之一

a6e4382180188b4d7eeb70e471044062.png

一、LINUX下的动态库

在前面将动态库的知识初步学习了一下,本篇将着重讲一下Linux下的动态库的编写和使用。在gccK和 g++中,都提供了丰富的编译选项,用来给程序员编译动态加提供较多的选择。可是,对于新手来说,如此多的编译选项反而会让其感觉到迷茫。所以还是那句话,先从简单的入手,不要上去就啃大部头,省得忙个昏天黑地反而打击了自己的积极性。
Linux下的动态库以.so结尾,命名方式必须是以lib开头,中间填充自己想取得动态库的名字。比如你想生成一个叫order的排序库,那么最终编译时应该是lib+order+.so(liborder.so),这个样子。这一点不如Window做的好,它可以起任何在规范下的名字,不必要如此。
另外在库路径下经常可以看到以后缀*.so.2之类的方式的动态库,其实那只是一种软链接,当使用ls -al命令查看时,可以得到下面的结果:

-64.so

这就是说这个软链接,是链接到后面的那个真实具体的so库上的。通常这种用法是用来进行版本管理的,比如库的名字为了保持一致,但为了区分不同的版本,可以软件链接形成不同的符号文件。也就是说其命名的规则可以如下:

lib + name +.so+x.y.z  后面的xyz可以用来表示主次及发行版本号等,或者自己另有约定。
另外一个新手需要注意的是,一定要保证链接各方的版本一致性,这个既包括自己编写的迭代版本,又要保证平台版本,比如,X64平台的库和X32平台的库就不能同时使用,否则可能编译链接时就会报链接错误。

二、编译选项的基本介绍

在动态库中有两个基本选项:
1、-shared链接器选项
表示这是一个动态库,因为还有静态库,所以要区分一下。或者说有此选项会生成一个共享目标文件,它可以和其它目标文件连接。

2、 -fPIC编译选项
前面已经说过,这个意思是编译与位置无关的代码。在32位上可忽略,但是在X64平台上还是要使用的。
这里有个小细节需要注意一下,有细心的可以在一些资料中看到-fpic全小写的这个编译选项,这二者有什么不同呢?相同点如前文所述都是生成与位置无关的代码,重点在不同点,如果全局偏移表(GOT)大小超过计算机的最大值,则-fpic不起作用,这时候儿就得用-fPIC重新编译。其中GOT的大小依据不同的OS则有不同,比如X86上没有限制(这意味着二者在X86是等同的),而在AArch64上为28K,其它一些具体的也有不同。换句话说,如果你在X86平台上编程,可以忽略这个不同。
但是为了安全起见,建议都使用-fPIC来编译自己的动态库,防止出现跨平台使用时的一些不容易查找的问题。
3、-W 警告信息参数 -Wl选项option传递给链接器
这个有比较多的用法,包括-Wall,看名字就知道,把-W开头的警告大部分都给合到一起了,但有一部分仍然排除。具体的可看一下GCC的资料。
-Wl其实就是把相关的链接参数选项传递给链接器,如果option中包含逗号就分割一下。

其实,编译过程中仍然会有不少的编译选项和链接选项,整个GCC的文档有几十页,有兴趣的可以看看,但不要太过沉迷进去。其实好多一般是用不到的。

三、应用与例程

1、动态库的生成
动态库的生成过程中有几个棘手的问题,第一个是c++的改名机制,在前面的文章里提到过c++编译器在编译源文件过程中会复用某种规则修改程序中的名字,这就需要链接器对其进行适应,一般有两种方法,一种是建立名字修改的规则,也就是形成一种标准技术,但这有点为难。不过好在主流的编译器厂商不多,而实际广泛应用的也不过三两种,这还可以忍受。另外一种就是像C编译器一样,复用一些关键字来告诉编译器不要改名。
第二个在前面也提到过,就是静态变量的初始化顺序,也包括全局变量,这就需要编写时尽量减少静态和全局变量的使用。使用时,也尽量要集中它们到单独的文件中,不要满天飞的定义全局和静态变量。对于c++可以让类提供一个单独的Init函数来处理这些定义顺序,人为的控制顺序。或者也可以采用某种特定的方式,通过程序来访问,比如采用饱汉模式提前定义好全局或者静态对象,其它访问都通过此对象来完成。
最后一个是模板的问题,模板的引入本身是为了方便,但模板的静态编译又使其需要的条件更加严格一些,而模板实例化或者说特化的模式又会产生不同的代码。基础类型如int等还比较好处理。但是当遇到一些具体的对象类型时,就比较麻烦了,特别是在动态库中,解决的方法有两类,一类是预先使用弱符号来创建模板特化的代码,编译器生成所有的特化代码和相对就把弱符号,在链接时如果没有用到就抛弃。另外一类是,链接器在结束链接前都不考虑模板特化的代码。只有当其它的链接任务都完成后,再检查代码,确实具体的模板特化的代码,并复用编译器生成,并插入到可执行文件。

2、动态库调用动态库
动态库可以调用动态库,甚至可以形成链式结构,但这时候儿就需要考虑加载的顺序了。实际应用动态库的场景不外乎以下几种:程序单独调用一个动态库;程序单独调用N个动态库,但库之间没有联系;程序调用多个动态库,动态库之间有依赖,即有互相调用的现象。

3、例程
前文加载了一个例程,基本可以直接移植过来,但这里提供了一套额外的动态调用的方式:

#include 

动态调用的方式:

#include 

用下面的语句编译:

g++ 

说明一下:
rdynamic用来通知链接器将所有符号添加到动态符号表中,以方便dlopen的实现向后的跟踪,-ldl表示一定将dlib库链接于此程序。
dlopen用来打开相关的库对象,通过对名字的判定来加载具体的动态库。它有两个参数,RTLD_NOW表示在此函数调用完成所有必要后再定位而RTLD_LAZY表示在需要时再进行定位,另外还有RTLD_LOCAL 和RTLD_GLOBAL 模式,用来对加载符号的限定
dlsym    用来将dlopen取得的对象来得到相关函数在对象文件中的符号的地址
dlerror    返回上一次出现错误的字符串错误
dlclose    关闭目标文件
在上面的代码中,会报func error,也就是说无法找到Compare这个函数,为什么呢,如果是C文件用GCC编译就不存在这个问题,如果用G++编译就会有这个问题,怎么办呢?用nm命令看一下这个库文件:

$ nm libcompare.so
0000000000201030 B __bss_start
0000000000201030 b completed.7409
                 U __cxa_atexit@@GLIBC_2.2.5
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000000640 t deregister_tm_clones
00000000000006b0 t __do_global_dtors_aux
0000000000200dd0 t __do_global_dtors_aux_fini_array_entry
0000000000201028 d __dso_handle
0000000000200dd8 d _DYNAMIC
0000000000201030 D _edata
0000000000201038 B _end
0000000000000770 T _fini
00000000000006f0 t frame_dummy
0000000000200dc0 t __frame_dummy_init_array_entry
0000000000000868 r __FRAME_END__
0000000000201000 d _GLOBAL_OFFSET_TABLE_
000000000000075a t _GLOBAL__sub_I_compare.cpp
                 w __gmon_start__
000000000000077c r __GNU_EH_FRAME_HDR
00000000000005e0 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000000670 t register_tm_clones
0000000000201030 d __TMC_END__
0000000000000711 t _Z41__static_initialization_and_destruction_0ii
00000000000006f5 T _Z7Compareii
                 U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
                 U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
0000000000000779 r _ZStL19piecewise_construct
0000000000201031 b _ZStL8__ioinit

可以看到c++的编译器进行了改名机制,把Compare改成了_Z7Compareii,然后把函数名字改成这个,再重新编译,就OK了。(如果遇到库找不到的问题,可以参考上一篇文章的解决方法)
可以根据实际的情况来使用不同的加载方式。

四、总结

后面会根据动态库的应用如互相调用、调试(查看工具等)、调用静态库等逐步展开各种情形的学习,并对其一些内部机理进行分析,从小处一点一滴的学起,不要想着一步登顶,用侯捷的话来说“勿在浮沙筑高台”。

973c83405970e6ac3ad00a1ad9bda82c.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值