linux下C/C++开发小记1


前言

本文主要是记录了在用QtCreator作为IDE在linux下进行共享库开发时遇到的问题及解决方法,其中也包含自己对linux在编译,链接,加载运行环节的一些理解。


以下是本篇文章正文内容

一、linux下的共享库

linux下的共享库通常以.so作为后缀,当然也可以添加版本号。使用共享库可以大大减少内存及磁盘空间,使得代码段可以在多个进程间共享,而数据段则在每个进程空间保持一份copy。

二、动态链接和静态链接

在linux下开发共享库(.so)或者可执行程序时,源文件(.c,.cpp)可能不止一个,尤其是在工程较大时,编译器都是以单个源文件作为编译单元最终得到目标文件(.o),最后由链接器将多个.o以一定的方式(过程很复杂)链接成最终的共享库或者可执行程序,这个过程就是静态链接的过程。
动态链接是指,在程序加载运行时,动态链接器会首先将依赖的共享库按照一定的规则加载到进程空间中,在这个过程会完成相关符号的解析和重定位工作。
需要注意的是这里说的符号指的是变量和函数。

三、显示动态链接

所谓的显示动态链接是指,不依赖动态链接器,在需要的时候程序主动去调用dlopen,dlsysm等接口实现共享库的载入和符号的解析。

四、pro中的编译、链接参数

下面会针对主要的几个参数做下说明,需要注意的是在生成可执行文件和共享库文件时,有些参数的作用是不同的,以下是两个pro的例子(仅做说明用)。
可执行程序下的pro:

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp

QMAKE_LFLAGS += -L$$PWD/../lib/
QMAKE_LFLAGS += -Wl,-rpath=$$PWD/../lib/ -Wl,-rpath=$$PWD/

#QMAKE_LFLAGS += -Wl,-Bsymbolic
#LIBS+=  -lmylibA -lmylibB
LIBS += -lmylibB -lmylibC


unix {
    target.path = $$PWD/../lib
    INSTALLS += target
}

共享库中的pro:

TEMPLATE = lib
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
TARGET = mylibB
CONFIG += plugin
SOURCES += \
    mylibB1.cpp


#QMAKE_CXXFLAGS += -fvisibility=default

QMAKE_CXXFLAGS += -fvisibility=hidden
QMAKE_LFLAGS += -Wl,-Bsymbolic
QMAKE_LFLAGS += -Wl,-rpath=./tmp

OBJECTS_DIR     = objB  #指定目标文件(obj)的存放目录

LIBS +=  -L$$PWD/../lib/tmp -lmylibA

unix {
    target.path = $$PWD/../lib
    INSTALLS += target
}

1.QMKAE_CXXFLAGS和QMAKE_LFLAGS

QMAKE_CXXFLAGS变量是给编译器传入相关参数,当然影响的也就是编译阶段;
QMAKE_LFLAGS变量是给链接器传入相关参数,影响的是链接阶段。

2.编译器参数-fvisibility

-fvisibility=hidden,表示符号对外部默认是不可见的,也就是说模块内部(.so)的符号不能被其他模块使用(看不见当然无法使用),当隐藏不需要对外公开的符号时,可以有效避免自己内部使用的符号对外部其他模块产生干扰,产生干扰的原因主要是由于全局符号介入当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略)导致的。如果此时需要导出某些符号,例如函数接口,则需要配合__attribute__((visibility(“default”)))使用,如下所示:


int __attribute__((visibility("default"))) myInt1;
int __attribute__((visibility("default"))) myInt2 = 0xB;
int __attribute__((visibility("default"))) myInt3 = 0xb;
int mySubFromA();
int undefineFunc();

int __attribute__((visibility("default"))) myadd()
{
    cout << "Hello World! " << __FILE__<< endl;
    //undefineFunc();
    return 0;
}

int __attribute__((visibility("default"))) mysub() {
    cout << __FILE__ <<": "<<__FUNCTION__<<endl;
    return mySubFromA();
}

注意如果没有QMAKE_CXXFLAGS += -fvisibility=hidden,则编译器默认是 -fvisibility=default,表示所有的符号时对外部可见的。另外,-fvisibility还有其他参数(本人没有研究过)。

3.编译器参数 -shared、-fPIC

截图来自工程编译时
从图中可以看到当我用QtCreator去创建动态库时,在编译阶段会默认加上-fPIC和-shared参数。-fPIC参数表示生成与地址无关的代码,即实现代码段在多进程见可以共享的目的;-shared表示生成的是共享库。看相关文章了解到,现代的编译器当指定-shared参数时会默认的加上-fPIC参数,但是为了保险起见在需要生成与地址无关的共享库时,建议手动添加-fPIC参数。

4.链接器参数 -Wl,-Bsymbolic

改参数作用于链接阶段,让模块优先使用自己内部定义的符号,这样做的好处也是避免全局符号介入带来的影响,例如mylibA.so中有个全局变量var,mylibB.so中也有同名的全局变量var,程序在加载运行时如果先加载了模块mylibB.so,那么全局变量则以B中的var为准,这样必然会对A中var的使用产生影响,同名的全局函数亦是如此,此时若在生成mylibA.so时使用了-Wl,-Bsymbolic,则模块A中就会优先使用自己模块内部的符号,达到同其他模块符号隔离的目的。
那么问题来了,编译参数-fvisibility貌似也能达到相同的目的,其实是有很大区别的:
1.-fvisibility=hidden是将自己内部符号隐藏起来,对外不可见,这样就不会对其他模块产生干扰,这种方法是一种被动方式,即不打扰别人;
2.-Wl,-Bsymbolic是自己使用自己内部的符号,当然内部的符号也可能回干扰到别人,但能实现自给自足,是一种主动的方式;
3.这两种方式作用的阶段不同,隐藏符号作用于编译阶段,而后者作用于链接阶段。

5.链接器参数 -Wl,-rpath=相对路径或绝对路径

关于rpath和rpath-link的详细区别可以查看以下文章:
1.《关于rpath》
2.《rpath和rpath-link》
总结起来,rpath会在程序运行加载库时,以及编译链接时起作用;而rpath-link只作用于编译链接阶段。值得注意的是,使用这两个参数在编译生成可执行文件和共享库时,效果是不同的:
1.编译生成共享库时使用-rpath:此时即便指定了查找依赖库的路径,在编译时也不会在指定路径下去查找,但是会把rpath的路径写到生成的共享库中,如下图所示:
在这里插入图片描述
这个路径会在程序加载的时候起作用,链接器会根据这个路径寻找该模块的依赖库。
2.编译生成共享库时使用-rpath-link:指定的路径不会产生任何作用;
3.编译生成可执行程序时使用-rpath:在编译时会从指定的路径查找相关依赖库,在实际运行时也会从指定的路径查找依赖库,也会将路径写入可执行文件,如下图所示:
在这里插入图片描述
从上图可以看出支持传入多个路径;
4.编译生成可执行程序时使用-rpath-link:只在编译时会从指定的路径查找相关依赖库,不会对运行阶段产生任何影响。

五、全局符号的载入顺序

开门见山的讲,搜索顺序分为两种:广度优先和深度优先,具体哪种取决于链接器,这里指的是动态链接器,一般采用广度优先算法。摘自书中的话:
在这里插入图片描述
举例说明下:
可执行程序myExe,在编译时指定LIBS+= -lmylibB -lmylibC注意这两个模块也是有先后顺序的,B在前C在后),即依赖于libmylibB.so和libmylibC.so,而libmylibB.so又依赖于libmylibA.so。在程序加载时,现将myExe中的全局符号载入全局符号表中,再去载入B模块,完事C模块,最后找A模块,如果有同名全局符号先被载入则不再去加载其他模块中的全局符号。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值