专栏算法工具链FAQ Communication

FAQ Communication

DR_KAN2023-09-26
268
0

Q1:编译链接communication库时提示fast相关的一些符号未定义

需要链接fastdds相关的依赖库。

在build.properties中添加com.hobot.native.bole.third-party:fast-dds:2.1.1:zip:true拉取相关的依赖库和头文件。

链接communication.so库时,只需要再链接fastrtps库。

链接libcommunication-static.a时,需要根据平台链接以下相关依赖库:


Q2:升级communication库之后,在其他模块中提示communication相关符号未定义,该如何处理?

可能是因为communication更新版本时,修改了接口定义,程序中有其他模块依赖了老版本的communication动态库。

可以通过让其他模块同步更新communication动态库解决问题。


Q3:Windows下debug编译时,出现在 fastrtps\utils\fixed_size_bitmap.hpp 中的编译错误,该如何处理?

可能是项目中使用了Windows系统头文件(如winsock2.h、windows.h等),并且包含这些头文件的顺序在communication的头文件之前。

Windows系统头文件中定义了max和min的宏,fastdds的这个文件中使用了std::max和std::min函数。但是fastdds中没有对std::max和std::min加括号,或者禁用windows的max和min的宏,导致了windows的宏和std::min,std::max的冲突。

虽然communication的头文件中定义了NOMINMAX这个宏禁用max和min宏,但只能保证宏定义之后的代码生效。

如果项目中使用Windows系统头文件的地方在communication的头文件之前,那么项目里面还是有了max和min这个宏,会和fastrtps\utils\fixed_size_bitmap.hpp中std::max和std::min冲突。而fastdds的源码不能随意修改。

可以通过在项目中包含windows系统头文件前加上宏定义#define NOMINMAX解决问题。

Q4:程序在执行的时候,crash在sub的回调函数里面,该如何处理?

sub回调函数里面的资源生命周期是由用户管理的。要保证sub的回调函数被调用时,这些资源已经被创建好了或者没有被释放。


Q5:communication的链路复用规则是怎样的?

创建communication的通信对象时,如果传入的participant_id是相同的,那么基于相同participant_id创建的communication对象底层的链路就是公用的。

如果配置文件linK_info一致,但是id不同,基于不同的id创建communication的多个通信对象则无法实现链路复用。以zmq tcp协议举例,如果是bind模式,则后面创建的communication对象会失败(报端口已经被占用的错误);如果是connect模式,则会重新创建一条新的链接。


Q6:communication的pub接口耗时较多的原因可能是什么?

communication的pub是异步接口,一般情况下不会花费比较多的时间。

在调用pub接口时会执行message中meta部分序列化的操作(除了进程内通信方式)。如果pub接口的调用耗时过长,可以检查meta部分数据量是否过于庞大或者结构过于复杂,从而导致序列化的时间花费比较多。

可以考虑对meta部分的结构进行优化,从而减少序列化需要的时间。


Q7:在我的X3/J3板子上无法运行communication X3/J3的库,报错信息为undefined symbol: hb_mem_module_open,该如何处理?

原因是communication中使用的hbmem的库比X3/J3板子上的hbmem库版本新,在板子上的hbmem库中没有communication中使用的hbmem的符号。

解决办法:

  1. 升级镜像(2022032115_rel-br21_211210_PATCH1及以上版本)。

  2. 如果镜像不能升级,并且不需要使用hbmem共享内存的通信方式,可以使用artifactory上的communication-pilot库,这版本是去除hbmem功能的communciation库。


Q8:在板端执行程序时,出现 Multicast init失败日志,该如何处理?

原因与当前板子网络配置有关,多播IP地址没有加入到路由表中,导致无法多播通信。

可使用如下命令将多播地址加入到路由表中:


Q9:communication V1.x.x之后的版本,使用RegisterParticipantExtInfo接口注册的pariticipant_id,创建通信实例失败,error_code返回-10111003,该如何处理?

原因是因为communication V1.x.x之后,communication的zmq和pcie使用插件的方式加载,所以在调用RegisterParticipantExtInfo接口的时候,需要先加载插件。可以在配置文件中加载,也可以通过接口加载。

通过配置文件加载:

通过接口加载:


Q10:communication V1.x.x之后版本,win平台使用communication debug库crash在插件里面,该如何处理?

可能的原因:communication使用的是debug版本,加载的插件库却是release版本。communication库和插件库都会用到hlog的动态库,communication debug库链接的是hlog-debug.so,release插件库链接的hlog.so,这样可能导致communication debug和release插件库的hlog符号冲突导致crash。

解决方法:加载的插件库也使用debug版本(修改plugin_file):


Q11:应用自定义了信号处理句柄,且信号处理函数中调用了hobot::communication::DeInit(),ctrl+c时crash或者卡住在communication调用栈?

信号处理是一个异步过程,干扰了程序的正常执行流程,使得程序发生了各种异常状况。例如在主线程中,hobot::communication::Init()尚未完成时,或者communication通信实体创建尚未完成时,发射信号打断主线程当前的活动,主线程转而调用信号处理句柄,如在信号处理句柄中调用了hobot::communication::DeInit(),此时会发生死锁现象。以上举例分析仅仅表达了一种由信号处理造成的可能的异常行为,实际上信号处理造成程序的混乱情形可能更加复杂(linux下man 7 signal-safety举了一个通俗易懂的例子)。因此优雅的在接收到退出信号(如SIGINT,SIGTERM等)时执行信号处理函数,并等待进程中的所有线程完成清理工作,需要遵守一定的编程规范。以下为如何优雅地处理ctr+c信号的并安全的退出进程的描述,部分内容参考自https://thomastrapp.com/posts/signal-handlers-for-multithreaded-c++

linux平台下,为了优雅捕获ctrl+c信号,我们需要启用一个专门的线程,用于捕获ctrl c信号和执行进程退出前的清理工作,而其它线程屏蔽ctrl c信号的处理,这种信号处理的原则非常经典。具体的,在这一步,我们可以在main线程一开始处设置信号屏蔽集,将ctrl c 对应的SIGINT信号添加进信号屏蔽集,并且使用pthread_sigmask将设置的信号集进行屏蔽处理,pthread_sigmask的作用不仅仅使主线程能够忽略ctrl c的影响,也是的所有其它创建出来的子线程能够忽略ctrl c信号。而前面描述的专门的线程则是主动调用sigwait操作,显示地进行ctrl c信号捕获,捕获到ctrl c后,进行进程退出前地资源清理工作。这个专门的线程可以通过std::async创建,也可以使用std::thread创建,只要其在捕获信号,执行进程前退出的清理工作后,能够通知到主线程及时退出进程即可(如标记量或者条件变量等)。具体的example可参考communication example/signal_handler.cpp示例。 流程总结如下:

  1. main 线程启动后立即设置信号屏蔽集,将ctrl c对应的SIGINT 添加到信号屏蔽集

  2. 设置完信号屏蔽集后,调用pthread_sigmask api,使得后面创建出的子线程能够继承该信号屏蔽集,忽略ctrl c信号的处理

  3. 自定义用户自己的信号处理函数,并启动新线程执行该信号处理函数(使用std::async或std::thread),在信号处理函数中,首先显示调用sigwait api,等待ctrl c事件发生,然后再执行用户进程退出前的清理工作。

  4. 这样操作下来,可以保证其它任何线程不会被ctrl c信号打断其正常的执行流程,只有专门的线程会去处理ctrl c信号

  5. 专门的线程执行完进程退出前的清理工作后,通知主线程清理工作完成,主线程可以退出了,从而整个进程安全且优雅的退出。

  6. windows平台下尚未发现普通的信号处理方式会发生阻塞或者crash在communication上的问题,可能其信号处理机制与linux不同,因此暂时不做特殊处理。

补充: 信号处理句柄中最好仅使用 asyn-signal-safe的函数(定义见man 7 signal-safety),例如含由锁的、访问全局变量等的不可重入函数,都是asyn-signal-unsafe函数,不应当在信号处理句柄函数中使用。当然这个要求比较苛刻,对代码设计要求较高。总之,优雅的从信号处理函数中退出不是一件简单的事情。


Q12:创建pcie通信对象失败,日志为 platform not support pcie

检查使用的communication库是否支持pcie通信。

请确认库是否使用正确:

  • 迭代4中支持pcie通信的communication库为m5p2平台;

  • 迭代3和迭代2中支持pcie通信的communication库为j5平台。

还可以看下communication的库依赖中是否有pcie相关库,如果没有依赖pcie相关的库,那么请换为支持pci的库。


Q13:pcie RC2EP通信时出现日志 recv has no more phyaddr buffer!!!

这是由于发送端预先申请的物理地址的内存池被使用完了,但是接收端还没有释放接收到的消息导致的。

解决方法:

  1. 尝试增大发送端的内存池大小(增大配置文件的recv_buffer_size)。

  2. 查看接收端的callback是否阻塞了,导致消息长时间没有释放,但是pub端一直发送,导致内存池被耗尽。


Q14:pcie EP2EP通信两端无法通信,该如何处理?

检查两端的映射的物理地址是否正确。

使用hbpcie_test 0 0命令查看当前pcie设备的相关信息,包括设备id、每个bar的CPU和PCI地址。

具体的设置方法,可以参考开发者指南中pcie EP2EP部分的说明。


Q15:不同通信对象如何复用同一条sdio链路?

communication中sdio链路的复用规则和communication其他协议的链路复用规则是一致的。

创建communication的通信对象时,如果传入的participant_id是相同的,那么基于相同participant_id创建的communication对象底层的链路就是公用的。

如果配置文件linK_info一致,但是id不同,基于不同的id创建communication的多个通信对象则无法实现链路复用。如果sdio链路按照这种错误的方式配置了,那么可能所有的链路都在不断尝试抢占相同的链路,表现为所有的链路都无法通信。


Q16:使用sdio链路无法通信,报错信息为invalid argument,该如何处理?

检查sdio的配置文件中ap(Application Processor)和cp(Co-Processor)的设置是否正确。

ap是指车机上android应用端处理器。程序运行在Android时,link_info中的信息设为ap;

cp是指车机上的j2/j3协处理器。程序运行在j2/j3时,link_info中的信息设为cp。


Q17:不同进程是否可以使用相同的sdio节点?

不可以。

sdio的链接是一对一的,且不支持类似TCP的1对多链接方式。

如果一对sdio的链接已经建立后,其他进程无法attach到已有的链接上。因此不同进程不能复用相同的链路。

如果链路比较紧张,可以增加bif虚拟链路。


Q18:sdio的pub出现日志SyncWrite write data failed! sdio buffer is not enough!

可能的原因是sdio的发送数据过快或者数据量过大,导致communiation内部的sdio消息的缓存满了(消息缓存数量为500),缓存满了之后会丢旧的消息。

需要注意的是sdio链路的传输速度大约为20~30MB/s,这是链路本身的特性决定的,传输速度比较慢。如果pub的速度过快或者数据量过大,sdio链路来不及处理,就会导致缓存满了的问题。

出现这个问题后,需要考虑sdio的链路特性,合理分配sdio上传输的数据速度和大小。


Q19:shm和zmq_tcp同样的通信条件下(相同的pub频率和数据大小),使用shm反而出现大量丢包现象?

正常情况下发送较大数据时,shm协议的性能是明显高于zmq_tcp协议的,出现问题中描述的现象,可能的原因是:共享内存不够用了,或者inode数量不够用,可以适当清理/dev/shm目录中的内容,或者调大/dev/shm大小和inode数量,示例如下:

算法工具链
官方教程
评论0
0/1000