16

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

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

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

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

- - 编辑 - -

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

4

7 回答 7

11

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

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

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

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

10 ms @ 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 毫秒延迟的原因。

于 2012-10-30T09:51:27.387 回答
7

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

- - 编辑 - -

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

于 2012-11-15T10:31:39.330 回答
6

linux 上的串行端口被“包装”到 unix 风格的终端结构中,它会以 1 个滴答延迟(即 10 毫秒)击中您。如果有stty -F /dev/ttySx raw low_latency帮助,请尝试,但不能保证。

在 PC 上,您可以直接与标准串口通信,setserial /dev/ttySx uart none从串口硬件中解绑 linux 驱动程序,并通过inb/outb端口寄存器控制端口。我试过了,效果很好。

缺点是当数据到达时你不会得到中断,你必须轮询寄存器。经常。

你应该能够在 arm 设备端做同样的事情,在异国串行端口硬件上可能要困难得多。

于 2013-01-22T16:21:18.573 回答
3

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

ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);
于 2017-04-19T13:00:54.627 回答
1

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

你确定你的硬件可以做到这一点吗?发现具有引入许多字节延迟的缓冲区设计的 UART 并不少见。

于 2012-10-29T17:54:56.580 回答
1

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

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

我用 ASYNC_LOW_LATENCY 在大约 20 毫秒的时间内完成了大约 5 个事务(到 4 个设备)。这包括到慢速戳设备的两个事务(4 毫秒响应时间)。

没有ASYNC_LOW_LATENCY总时间约为 60 毫秒。

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

我目前正在使用自制的 USB 适配器,我可以将适配器本身的延迟设置为我想要的任何值。将其设置为 200 µS 可将 20 mS 的时间再缩短一个 mS。

于 2017-09-04T12:54:34.673 回答
0

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

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

与线路上的时间相比,系统调用开销微不足道,因此 select() 与 poll() 等不太可能产生影响。

于 2012-10-30T10:15:57.057 回答