Q1:编译链接communication库时提示fast相关的一些符号未定义
需要链接fastdds相关的依赖库。
链接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的源码不能随意修改。
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的符号。
解决办法:
升级镜像(2022032115_rel-br21_211210_PATCH1及以上版本)。
如果镜像不能升级,并且不需要使用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示例。 流程总结如下:
main 线程启动后立即设置信号屏蔽集,将ctrl c对应的SIGINT 添加到信号屏蔽集
设置完信号屏蔽集后,调用pthread_sigmask api,使得后面创建出的子线程能够继承该信号屏蔽集,忽略ctrl c信号的处理
自定义用户自己的信号处理函数,并启动新线程执行该信号处理函数(使用std::async或std::thread),在信号处理函数中,首先显示调用sigwait api,等待ctrl c事件发生,然后再执行用户进程退出前的清理工作。
这样操作下来,可以保证其它任何线程不会被ctrl c信号打断其正常的执行流程,只有专门的线程会去处理ctrl c信号
专门的线程执行完进程退出前的清理工作后,通知主线程清理工作完成,主线程可以退出了,从而整个进程安全且优雅的退出。
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!!!
这是由于发送端预先申请的物理地址的内存池被使用完了,但是接收端还没有释放接收到的消息导致的。
解决方法:
尝试增大发送端的内存池大小(增大配置文件的recv_buffer_size)。
查看接收端的callback是否阻塞了,导致消息长时间没有释放,但是pub端一直发送,导致内存池被耗尽。
Q14:pcie EP2EP通信两端无法通信,该如何处理?
检查两端的映射的物理地址是否正确。
具体的设置方法,可以参考开发者指南中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数量,示例如下:
