繁体   English   中英

Linux上的低延迟串行通信

[英]Low latency serial communication on Linux

我正在Linux上的串行端口上实现协议。 该协议基于请求应答方案,因此吞吐量受将数据包发送到设备并获得应答所花费的时间限制。 这些设备主要基于Arm,并且运行Linux> = 3.0。 我无法将往返时间缩短到10ms以下(115200波特,8个数据位,无奇偶校验,每条消息7个字节)。

哪些IO接口将为我带来最低的延迟:选择,轮询,epoll或使用ioctl手动轮询? 阻塞或非阻塞IO是否会影响延迟?

我尝试用setserial设置low_latency标志。 但这似乎没有效果。

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

----编辑----

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

请求/应答方案往往效率低下,并且在串行端口上很快显示出来。 如果您对吞吐量感兴趣,请查看窗口协议,例如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毫秒延迟的原因。

与更多的工程师讨论了该主题之后,我得出的结论是,该问题在用户空间中无法解决。 由于我们需要跨网桥进入内核领域,因此我们计划实现一个内核模块,该模块讨论我们的协议并提供小于1ms的延迟。

-编辑-

原来我是完全错误的。 所需要做的只是增加内核滴答率。 默认的100个滴答声增加了10ms的延迟。 1000Hz和串行过程的负整数值给了我想要达到的时间特性。

linux上的串行端口被“包装”为unix样式的终端结构,这给您带来1滴答的延迟,即10毫秒。 尝试如果stty -F /dev/ttySx raw low_latency帮助,尽管不能保证。

在PC上,您可以硬核直接与标准串行端口通信,发出setserial /dev/ttySx uart none来从串行端口hw解除绑定Linux驱动程序,并通过inb/outb控制端口到端口寄存器。 我已经尝试过了,效果很好。

不利的一面是当数据到达时您不会得到中断,而必须轮询寄存器。 经常。

您应该能够在arm设备端执行相同的操作,在异国情调的串行端口硬件上可能要困难得多。

这些系统调用均不影响延迟。 如果您想尽可能快地从用户空间读取和写入一个字节,那么您确实比简单的read()/write()对要好。 尝试用另一个用户空间进程中的套接字替换串行流,看看延迟是否有所改善。 如果没有,那么您的问题就是CPU速度和硬件限制。

您确定您的硬件完全可以做到这一点吗? 找到具有缓冲器设计的UART并引入许多字节延迟的情况并不少见。

这是setserial用来在端口的文件描述符上设置低延迟的方法:

ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);

简而言之 :使用USB适配器和ASYNC_LOW_LATENCY。

我在Modbus上以115.2 kbs使用了基于FT232RL的USB适配器。

通过ASYNC_LOW_LATENCY,我总共约20毫秒获得了约5笔交易(到4台设备)。 这包括到慢速设备的两次事务处理(响应时间为4毫秒)。

如果没有ASYNC_LOW_LATENCY则总时间约为60 ASYNC_LOW_LATENCY

使用FTDI USB适配器, ASYNC_LOW_LATENCY芯片本身的字符间计时器设置为1 mS(而不是默认的16 mS)。

我目前正在使用自制的USB适配器,并且可以将适配器本身的延迟设置为所需的任何值。 将其设置为200 µS,可将原来的20 mS减少另外的ms。

在这些线速下,无论如何检查准备情况,您都不会看到那么大的延迟。

您需要确保串行端口处于原始模式(因此您要进行“非常规读取”),并且正确设置了VMIN和VTIME。 您想确保VTIME为零,以使字符间计时器永远不会启动。我可能首先将VMIN设置为1并从那里进行调整。

与联机时间相比,系统调用开销没有什么用,因此select()与poll()等不太可能有所不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM