我正在编写一个代码,它每1ms从服务器接收一次原始以太网数据包(没有TCP/UDP(。对于收到的每个数据包,我的应用程序必须回复 14 个原始数据包。如果服务器在发送计划每 1 毫秒一次的数据包之前没有收到 14 个数据包,则服务器会发出警报,应用程序必须爆发。服务器-客户端通信是一对一链接。
服务器是一个硬件 (FPGA(,它以精确的 1ms 间隔生成数据包。客户端应用程序在具有10G SolarFlare NIC的Linux(RHEL/Centos 7(机器上运行。
我的第一个代码版本是这样的
while(1)
{
while(1)
{
numbytes = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if(numbytes > 0)
{
//Some more lines here, to read packet number
break;
}
}
for (i=0;i<14;i++)
{
if (sendto(sockfd,(void *)(sym) , sizeof(sym), 0, NULL, NULL) < 0)
perror("Send failedn");
}
}
我通过在recvfrom
呼叫之前和之后获取时间戳(使用clock_gettime
(来测量接收时间,我打印这些时间戳的时间差,并在时差超过允许范围时打印它们900-1100 us。
我面临的问题是数据包接收时间在波动。像这样的东西(打印以微秒为单位(
Decode Time : 1234
Decode Time : 762
Decode Time : 1593
Decode Time : 406
Decode Time : 1703
Decode Time : 257
Decode Time : 1493
Decode Time : 514
and so on..
有时解码时间超过2000us,应用程序会中断。
在这种情况下,应用程序将在 2 秒到几分钟之间的任何位置中断。
到目前为止,我尝试过的选项。
- 设置与特定隔离核心的关联。
- 使用
SCHED_FIFO
将计划优先级设置为最大 - 增加套接字缓冲区大小
- 将网络接口中断关联设置为处理应用程序的同一内核
- 使用
poll(),select()
调用在recvfrom
上旋转。
所有这些选项都比代码的初始版本有了显著的改进。现在应用程序将运行 ~1-2 小时。但这仍然不够。
几点观察:
- 每当我在应用程序运行时将 ssh 会话带到 Linux 机器时,我都会得到大量这些解码时间打印(这让我认为通过其他 1G 以太网接口进行网络通信会对 10G 以太网接口产生干扰(。
- 应用程序在 RHEL 中(运行时间约为 2-3 小时(的性能优于 Centos(运行时间约为 30 分钟 - 1.5 小时(
- 对于具有相同操作系统的不同硬件配置的 Linux 计算机,运行时间也有所不同。
请建议是否有任何其他方法可以改善应用程序的运行时。
提前谢谢。
首先,您需要验证时间戳方法的准确性; clock_gettime。 分辨率是纳秒,但准确性和精度是有问题的。 这不是您问题的答案,但在继续之前会告知时间戳的可靠性。 请参阅CLOCK_REALTIME和CLOCK_MONOTONIC之间的区别?为什么CLOCK_MONOTONIC应该用于您的应用程序。
我怀疑大多数解码时间波动是由于每次解码的操作次数可变、操作系统的上下文切换或 IRQ 造成的。
每个解码的操作我无法评论,因为代码在您的帖子中已简化。 也可以分析和检查此问题。
可以轻松检查和监控每个过程的上下文切换 https://unix.stackexchange.com/a/84345
正如Ron所说,这些对网络来说是非常严格的时序要求。 它必须是一个孤立的网络,并且用途单一。 您关于在 ssh'ing 时解码超时的观察表明必须阻止所有其他流量。 考虑到单独的 NIC,这令人不安。 因此,我怀疑IRQ是问题所在。 请参阅/proc/interrupts。
为了在较长的时间间隔(数小时->天(内实现一致的解码时间,需要大大简化操作系统。 删除不必要的进程和服务、硬件,也许还可以构建自己的内核。 所有这些都是为了减少上下文切换和中断。 此时应考虑实时操作系统。 这只会提高解码时间一致的概率,不能保证。
我的工作是开发一个结合了FPGA ADC、PC和以太网的数据采集系统。 不可避免地,多用途PC的不一致意味着某些功能必须转移到专用硬件上。 考虑为 PC 开发应用程序与将其移动到硬件的优缺点。