本文已参与「新人创作礼」活动,一起开启掘金创作之路。
动态库中的单例出现多例问题
1. 单例模式是最常用的一种设计模式。然而如果一个单例类C在动态库A中,而另一个动态库B中也使用C,此时如果同时加载动态库,A,B;则会出现A,B中各有一个C对象。使得单例不再是单例。
解决方法:单例对象不要定义在.h中,定义在.cpp中。 整个程序要有一个主动态库,其他动态库依赖这个主动态库。这种单例就放在这个主动态库中。
so依赖so,怎么不用设置LD_LIBRARY_APTH
-Wl,-rpath=$ORIGIN
在bazel BUILD文件中:linkopts = ["-lpthread","-lgomp", "-lpython2.7", "-Wl,-rpath=$$ORIGIN"],
rpath可以指定加载时动态库搜索路径。一般加个. 和ORIGIN就是动态库自己的路径
bazel中 cc_library打包库时,没用到的变量函数会被strip掉
解决方法:
.bazelrc添加 build --nolegacy_whole_archive
cc_library中添加alwayslink = True,
多动态库符号冲突问题
比如Protobuf, 这相当于“动态库中的单例不出现多例问题”中单例被定义在两个动态库,如果这个单例是factory,那么有可能注册相同的东西,会冲突
解决方法: 这两个动态库都依赖protobuf的.h文件。不要把cc文件打包进去。
最终连接成二进制时,或者main函数所在二进制才依赖protobuf的cc文件。
另一种解法可以尝试使用linux 的dlopen时指定 RTLD_LOCAL模式加载
python以RTLD_GLOBAL方式加载扩展so
有时候想写个so, so里有个单例。 这个单例给其他so用。这时只能以RTLD_GLOBAL加载
而python默认import 是RTLD_LOCAL方式
import sys
import ctypes
old_flags = sys.getdlopenflags()
sys.setdlopenflags(old_flags|ctypes.RTLD_GLOBAL)
import xxx #这是要import的so
sys.setdlopenflags(old_flags)
del old_flags
注意,xxx这个so要尽量简单,不要依赖太多东西,否则和python里的符号冲突就完蛋了。
我一般会搞两个so. 把最核心的单例,factory打成so,用GLOBAL方式加载。其他功能代码,都依赖这个核心so, 并以LOCAL方式加载
依赖bazel大项目
如果你有一个小项目A,想依赖另一个复杂的大项目B(比如tensorflow). 最好把B的WORKSPACE文件和.bazelrc文件复制到A来改。 反过来会更复杂。
BAZEL踩坑
项目不复杂的话在顶层放个BUILD文件就行了,不要在每个目录下放BUILD文件了。
要发布的动态库用cc_library(name=libxxx.so, linkshared=True)来打包。
cc_import 来导入依赖的动态库和.h放在另一个cc_library里。
.bazelrc里注意这两个选项,当编译时找不到文件:
build --spawn_strategy=standalone
build --strategy=Genrule=standalone
.bazelrc里用
ABI问题
依赖动态库,打包库要注意ABI. 默认ABI=1. 如果ABI不同,链接时找不到符号。 undefined reference.