前言

Protobuf全称Protocol buffers,是Google研发的一种跨语言、跨平台的序列化结构的数据格式,是一个灵活的、高效的用于序列化数据的协议。使用protobuf时,既可以采用动态链接,也可以采用静态链接。因为protobuf本身有一个globalregistry。每个message type都需要去那里注册一下,而且不能重复注册。所以,假如你在A.DLL中定义了某些message type,那么B.DLL就只能从A.DLL的exported的DLL interface中使用这些message type, 而不能从proto文件中重新生成C/C++代码并包含到B.DLL里去。并且B.DLL也不能私自的去修改、扩展这个message type。

最近在调用tensorRT解析onnx模型的时候使用到了这个库,遇到了一些折磨了很久的坑,记录一下。

目录

    • 前言
    • 1、运行时内存溢出
    • 2、编译时链接库报错
  • 关于`GCC`的编译选项`fpic`/`fPIC`, `fpie`/`fPIE`

1、运行时内存溢出

在代码运行时,出现以下错误

[libprotobuf ERROR google/protobuf/descriptor_database.cc:641] File already exists in database: ais_msg.proto
[libprotobuf FATAL google/protobuf/descriptor.cc:2021] CHECK failed: GeneratedDatabase()->Add(encoded_file_descriptor, size):
terminate called after throwing an instance of ‘google::protobuf::FatalException’
what(): CHECK failed: GeneratedDatabase()->Add(encoded_file_descriptor, size):

原因:

有人说是因为需要通信多个进程模块都集成了相同的 *.pb.cc*.pb.h 文件进行编译(动态库或者执行文件),且在编译时通过动态库 libprotobuf.so 的方式进行链接,导致在运行时报错,通过网上查找得到:**protobuf 本身有一个 globalregistry。每个 message type 都需要去那里注册一下,而且不能重复注册。上述的 Add 错误就是因为注册失败,原因就是因为这几个中重复注册了(多份 *.pb.cc 实现)。

PS:也有可能是protobuf的版本原因,我这里出现了这个错误之后,尝试了网上说的各种方法都没办法解决,后来把protobuf的版本从11.4.3换成6.1.3就没报这个错误了。

解决办法:静态库编译,使用 libprotobuf.a,即多个编译目标通过静态库的方式链接.

在编译protobuf的时候改 configure 文件再执行 configure

// 改成下面的样子(不同版本位置不对,所以可以搜索 ac_cv_env_CFLAGS_set)
if test "x${ac_cv_env_CFLAGS_set}" = "x"; thenCFLAGS="-fPIC"
fiif test "x${ac_cv_env_CXXFLAGS_set}" = "x"; thenCXXFLAGS="-fPIC"
fi
$ ./configure --host=arm-linux --disable-shared CFLAGS="-fPIC -fvisibility=hidden" CXXFLAGS="-fPIC -fvisibility=hidden" --prefix=/home/auto/libSrc/protobuf-3.6.1_install
$ make -j;make install

参考:protobuf 的交叉编译使用

2、编译时链接库报错

**原因:**报错中说我们的工程是一个动态库,但是链接的protobuf是一个静态库,将静态库编译为动态库时,会引起问题,请重新编译protobuf

**解决方法:**加上-fPIC选项重新编译protobuf库即可。

在编译protobuf的时候改 configure 文件再执行 configure

// 改成下面的样子(不同版本位置不对,所以可以搜索 ac_cv_env_CFLAGS_set)
if test "x${ac_cv_env_CFLAGS_set}" = "x"; thenCFLAGS="-fPIC"
fiif test "x${ac_cv_env_CXXFLAGS_set}" = "x"; thenCXXFLAGS="-fPIC"
fi
$ ./configure --host=arm-linux --disable-shared CFLAGS="-fPIC -fvisibility=hidden" CXXFLAGS="-fPIC -fvisibility=hidden" --prefix=/home/auto/libSrc/protobuf-3.6.1_install
$ make -j;make install

关于GCC的编译选项fpic/fPIC, fpie/fPIE

fPIC 的全称是 Position Independent Code, 用于编译阶段,告诉编译器产生与位置无关代码,代码在被进程加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表(GOT)来实现。

编译共享库时使用:

-fPIC: Generate position-independent code if possible (large mode)
-fpic: Generate position-independent code if possible (small mode)

编译可执行程序时使用:

-fPIE: Generate position-independent code for executables if possible (large mode)
-fpie: Generate position-independent code for executables if possible (small mode)

注:还需要在ld时增加-pie选项才能产生这种代码。即gcc -fpie -pie来编译程序。单独使用哪一个都无法达到效果。

-pie; Create a position independent executable

关于large modesmall mode的说明:small指程序必须位于2GB以下的地址空间;large指对地址空间没有任何限制。

在编译某个库的时候,加 fPIC 选项不加 fPIC 选项能否生成动态链接库没有必然的联系,即使不加 fPIC 也可以生成 .so 文件,只是对于那种库的源文件又调用了其他的地方的代码,而这个段代码在全局内存上只能有一个实例的时候,如果在多个进程中都调用了这个库那么会产生多个实例就发生了冲突,例如:Protobuf库。所以,我们在编译共享库的时候,一般都加上fPIC 编译选项,这样多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统显然会把它们映射到同一块物理内存上,这样就不会发生冲突。

对于对于不加 fPIC,则加载 .so 文件时,需要对代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个 .so 文件代码段的进程在内核里都会生成这个 .so 文件代码段的 copy。每个 copy 都不一样,取决于这个 .so 文件代码段和数据段内存映射的位置。可见,这种方式更消耗内存,但是通常来说它的加载速度会更快。

Protobuf报错CHECK failed: GeneratedDatabase()->Add(encoded_file_descriptor, size):相关推荐

  1. caffe 报错 Check failed: error == cudaSuccess (77 vs. 0) an illegal memory access was encounteredcaffe

    caffe 报错 Check failed: error == cudaSuccess (77 vs. 0) an illegal memory access was encountered 训练时候 ...

  2. mxnet报错 Check failed: dshp.ndim() == 4U (3 vs. 4) : Input data should be 4D in batch-num_filter-y-x

    报错:mxnet.base.MXNetError: Error in operator conv0: [17:40:27] src/operator/nn/convolution.cc:152: Ch ...

  3. kafka消费者报错:Failed to add leader for partitions

    最近遇到过这种错误两次了,通过describe来看的时候是正常的,但是总会报错,猜测试网络问题.果不其然, 一次是一台kafka的机器的防火墙没有关掉,关掉之后就ok了. 另外一次是因为工程把主机配置 ...

  4. caffe 报错 Check failed: error == cudaSuccess (77 vs. 0) an illegal memory access was encountered

    之前从没有遇到这样的问题,找了三个小时的bug和资料后,在此处发现了解决方案,他是这么说的: In my case the "top" and "bottom" ...

  5. 【错误记录】Android Studio 向 GitHub 提交代码报错 ( Push failed: Failed with error: Could not read from remote )

    文章目录 一.报错信息 二.解决方案 一.报错信息 在 Android Studio 中首次向 GitHub 提交代码 , 报错 : Push failed: Failed with error: C ...

  6. mysql启动失败LSB_mysql启动报错:Failed to start LSB: start and stop MySQL

    报错信息: [root@youxx- bin]# service mysql status Redirecting to /bin/systemctl status mysql.service ¡ñ ...

  7. maven testNG打成jar包运行报错repackage failed: Unable to find main class

    一.maven testNG如何打jar包 1.pom文件引入插件 <build><plugins><plugin><groupId>org.apach ...

  8. oracle补丁报错10044,【案例】Oracle补丁 数据库打patch报错OPatch failed with error code 73...

    天萃荷净 运维DBA反映在给Oracle数据库安装patch补丁时报错OPatch failed with error code 73,分析原因为相关服务未关闭导致 打patch出现Copy fail ...

  9. SAP MIGO对工单做101收货,报错 - Check table TFBEFU_CR entry 10 does not exist – 对策

    SAP MIGO对工单做101收货,报错 - Check table TFBEFU_CR entry 10 does not exist – 对策 执行事务代码MIGO,移动类型101,对某工单执行入 ...

最新文章

  1. 一名 40 岁“老”程序员的反思~
  2. Python enumerate函数
  3. 【TensorFlow-windows】学习笔记三——实战准备
  4. Hibernate实体映射配置1(java@注解方式)
  5. info nano shutdown
  6. java generic new_java中generic实例详解
  7. java时间戳龙_Java时间戳与日期格式字符串的互转
  8. javascript代码混淆的原理
  9. 惯导系统模型及其仿真(五)
  10. 阿里巴巴代码规范 学习总结
  11. logistic逻辑回归
  12. Linux下通过虚拟网卡实现局域网 转发tcp/udp流量
  13. domoticz折腾指南--通过esp_easy固件控制继电器开关
  14. 相亲交友v6.7.7
  15. 如何彻底删除hao123主页?
  16. zigzag算法详解
  17. FFmpeg H264增加SEI
  18. 【创业复盘】关键假设三板斧
  19. 5G-Advanced最新进展:3GPP R18首批项目立项
  20. 面试谈薪资不要怂,6个技巧,助你轻松拿高薪!【吊打面试官系列】

热门文章

  1. 一些《集成电路与光刻机》笔记
  2. 全开源即时通讯(IM)系统 高仿微信,android移动应用开发
  3. 优秀的选择 从年货节“走马灯”看天猫的H5创意
  4. 在微信朋友圈冲浪必备的心灵免疫力
  5. 前端开发之走进Vue.js(入门者看过来)
  6. linux 免费教程下载,Linux系统入门教程
  7. Ajax库-认识服务器,URL地址,axios基本用法,响应状态码,业务状态码,接口测试工具
  8. 中职网络安全—隐藏信息探索
  9. SIM7600CE模块(GSM/GPRS)调试
  10. 计算机辅助数控编程交互图形,第8章-计算机辅助数控编程.ppt