Linux上的低延迟串行通信
Low latency serial communication on Linux
我正在Linux上通过串行端口实现一个协议。该协议基于请求-应答方案,因此吞吐量受到向设备发送数据包并获得应答所需时间的限制。这些设备大多基于arm,运行Linux>=3.0。我在将往返时间降低到10ms以下(115200波特,8个数据位,无奇偶校验,每条消息7个字节)时遇到了问题。
哪些IO接口会给我最低的延迟:选择、轮询、epoll或使用ioctl手动轮询?阻塞或非阻塞IO是否会影响延迟?
我试着用setserial设置low_latency标志。但它似乎没有任何效果。
我还有其他可以减少延迟的方法吗?由于我控制着所有的设备,甚至可以修补内核,但最好不要
----编辑----
串行控制器使用的是一个16550A。
请求/应答方案往往效率低下,而且它在串行端口上显示得很快。如果你对throughtput感兴趣,可以看看窗口协议,比如kermit文件发送协议。
现在,如果你想坚持你的协议并减少延迟,选择、轮询、读取都会给你大致相同的延迟,因为正如Andy Ross所指出的,真正的延迟是在硬件FIFO处理中。
如果你幸运的话,你可以在不打补丁的情况下调整驱动程序的行为,但你仍然需要查看驱动程序代码。然而,让ARM处理10kHz的中断速率肯定不会对整个系统性能有好处。。。
另一种选择是填充数据包,以便每次都达到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毫秒延迟的原因。
在与更多的工程师讨论了这个主题后,我得出结论,这个问题在用户空间中是无法解决的。由于我们需要跨越这座桥进入内核领域,我们计划实现一个内核模块,该模块与我们的协议进行对话,并为我们提供延迟<1毫秒。
---编辑---
原来我完全错了。所需要的只是提高内核的点击率。默认的100个刻度增加了10ms的延迟。1000Hz,串行进程的负的值给了我想要达到的时间行为。
linux上的串行端口被"包装"到unix风格的终端结构中,这会给你带来1个节拍滞后,即10ms。如果stty -F /dev/ttySx raw low_latency
有帮助,请尝试,但不能保证。
在PC上,您可以直接访问标准串行端口,发出setserial /dev/ttySx uart none
从串行端口hw解除linux驱动程序的绑定,并通过inb/outb
将端口控制到端口寄存器。我试过了,效果很好。
不利的一面是,当数据到达时,你不会得到中断,你必须轮询寄存器。经常
您应该能够在arm设备端做同样的事情,在异国情调的串行端口hw上可能会困难得多。
以下是setserial
在端口的文件描述符上设置低延迟的方法:
ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);
简而言之:使用USB适配器和ASYNC_LOW_LATENCY。
我在Modbus上使用了一个基于FT232RL的USB适配器,速度为115.2 kbs。
我使用ASYNC_LOW_LATENCY总共在大约20mS内获得大约5个事务(到4个设备)。这包括到慢戳设备的两个事务(4mS响应时间)。
在没有ASYNC_LOW_LATENCY
的情况下,总时间约为60ms。
使用FTDI USB适配器,ASYNC_LOW_LATENCY
将芯片上的字符间计时器设置为1毫秒(而不是默认的16毫秒)。
我目前正在使用一个自制的USB适配器,我可以将适配器本身的延迟设置为我想要的任何值。将其设置为200µS会使20 mS的再减少一mS
这些系统调用都不会对延迟产生影响。如果你想从用户空间中以尽可能快的速度读取和写入一个字节,你真的不会比简单的read()/write()
对做得更好。尝试用另一个用户空间进程的套接字替换串行流,看看延迟是否有所改善。如果他们没有,那么你的问题是CPU速度和硬件限制。
你确定你的硬件能做到这一点吗?UART的缓冲区设计引入了相当于许多字节的延迟,这并不罕见。
在这样的线路速度下,无论您如何检查准备情况,都不应该看到那么大的延迟。
您需要确保串行端口处于原始模式(因此您可以进行"非匿名读取"),并且VMIN和VTIME设置正确。你想确保VTIME为零,这样字符间计时器就不会启动。我可能会从将VMIN设置为1开始,然后从那里开始调整。
与连线上的时间相比,系统调用开销微不足道,因此select()与poll()等不太可能有什么不同。
- 如何仅为一个函数添加延迟
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 是否可以使用winusb同时与多个相同的usb设备进行通信
- 混淆了如何使用IDL与Ethovision进行通信
- 以在Qt中的IF语句中设置时间延迟
- C++ Boost::asio串行通信与Arduino无法写入
- 模板化类中静态成员的延迟初始化
- 在 Windows 8/10 技术中完全实时的屏幕捕获,没有延迟
- 我是否需要包含隐式使用/与 WindowsAPI 通信"Windows.h"?
- 将自定义函数传递到基抽象类中以延迟执行
- 通过TCP的PvP通信问题
- C++一个线程如何正确通信其任务已完成?
- 我希望改进或要求我目前的延迟/睡眠方法。C++
- 如何在 c++ 中延迟?
- 如何将 Firebase 与基于 Linux 的客户端应用配合使用,以便与服务器进行双向消息通信
- ZeroMQ:如何使用inproc减少多线程通信延迟
- 远程、IPC 和线程场景中微服务的低延迟通信
- Linux上的低延迟串行通信
- Linux中C++Tcp通信中的间歇性延迟
- Android到Windows的tcp通信延迟