Linux 上的低延迟串行通信

新手上路,请多包涵

我正在 Linux 上通过串行端口实现协议。该协议基于请求应答方案,因此吞吐量受限于将数据包发送到设备并获得应答所需的时间。这些设备大多基于 arm 并运行 Linux >= 3.0。我在将往返时间减少到 10 毫秒以下时遇到了麻烦(115200 波特,8 个数据位,无奇偶校验,每条消息 7 个字节)。

哪些 IO 接口会给我最低的延迟:select、poll、epoll 或使用 ioctl 手动轮询?阻塞或非阻塞 IO 会影响延迟吗?

我尝试使用 setserial 设置 low_latency 标志。但是好像没什么效果。

我还有什么可以尝试减少延迟的方法吗?由于我控制所有设备,因此甚至可以修补内核,但最好不要。

- - 编辑 - -

串行控制器使用的是 16550A。

原文由 JustMaximumPower 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 544
2 个回答

在与更多工程师讨论这个话题后,我得出的结论是,这个问题在用户空间中是无法解决的。由于我们需要跨过桥进入内核领域,我们计划实现一个内核模块,它与我们的协议对话并给我们提供 < 1ms 的延迟。

- - 编辑 - -

事实证明我完全错了。所需要的只是增加内核滴答率。默认的 100 个滴答声增加了 10 毫秒的延迟。 1000Hz 和串行过程的负值给了我想要达到的时间行为。

原文由 JustMaximumPower 发布,翻译遵循 CC BY-SA 3.0 许可协议

请求/应答方案往往效率低下,并且在串行端口上很快出现。如果您对吞吐量感兴趣,请查看窗口协议,例如 kermit 文件发送协议。

现在,如果你想坚持你的协议并减少延迟,选择、轮询、读取都会给你大致相同的延迟,因为正如 Andy Ross 所说,真正的延迟在于硬件 FIFO 处理。

如果幸运的话,您可以在不打补丁的情况下调整驱动程序的行为,但您仍然需要查看驱动程序代码。然而,让 ARM 处理 10 kHz 的中断率肯定不会有利于整体系统性能……

另一种选择是填充您的数据包,以便您每次都达到 FIFO 阈值。它还将确认是否是 FIFO 阈值问题。

10 毫秒 @ 115200 足以传输 100 个字节(假设为 8N1),所以您看到的可能是因为 low_latency 标志未设置。尝试

setserial /dev/<tty_name> low_latency

它将设置 low_latency 标志,内核在 tty 层中向上移动数据时使用该标志:

 void tty_flip_buffer_push(struct tty_struct *tty)
{
         unsigned long flags;
         spin_lock_irqsave(&tty->buf.lock, flags);
         if (tty->buf.tail != NULL)
                 tty->buf.tail->commit = tty->buf.tail->used;
         spin_unlock_irqrestore(&tty->buf.lock, flags);

         if (tty->low_latency)
                 flush_to_ldisc(&tty->buf.work);
         else
                 schedule_work(&tty->buf.work);
}

schedule_work 调用可能是您观察到的 10 毫秒延迟的原因。

原文由 shodanex 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题