简体   繁体   English

Linux上的低延迟串行通信

[英]Low latency serial communication on Linux

I'm implementing a protocol over serial ports on Linux. 我正在Linux上的串行端口上实现协议。 The protocol is based on a request answer scheme so the throughput is limited by the time it takes to send a packet to a device and get an answer. 该协议基于请求应答方案,因此吞吐量受将数据包发送到设备并获得应答所花费的时间限制。 The devices are mostly arm based and run Linux >= 3.0. 这些设备主要基于Arm,并且运行Linux> = 3.0。 I'm having troubles reducing the round trip time below 10ms (115200 baud, 8 data bit, no parity, 7 byte per message). 我无法将往返时间缩短到10ms以下(115200波特,8个数据位,无奇偶校验,每条消息7个字节)。

What IO interfaces will give me the lowest latency: select, poll, epoll or polling by hand with ioctl? 哪些IO接口将为我带来最低的延迟:选择,轮询,epoll或使用ioctl手动轮询? Does blocking or non blocking IO impact latency? 阻塞或非阻塞IO是否会影响延迟?

I tried setting the low_latency flag with setserial. 我尝试用setserial设置low_latency标志。 But it seemed like it had no effect. 但这似乎没有效果。

Are there any other things I can try to reduce latency? 我还有其他方法可以尝试减少延迟吗? Since I control all devices it would even be possible to patch the kernel, but its preferred not to. 由于我控制所有设备,因此甚至可以修补内核,但最好不要修补。

---- Edit ---- ----编辑----

The serial controller uses is an 16550A. 串行控制器使用的是16550A。

Request / answer schemes tends to be inefficient, and it shows up quickly on serial port. 请求/应答方案往往效率低下,并且在串行端口上很快显示出来。 If you are interested in throughtput, look at windowed protocol, like kermit file sending protocol. 如果您对吞吐量感兴趣,请查看窗口协议,例如kermit文件发送协议。

Now if you want to stick with your protocol and reduce latency, select, poll, read will all give you roughly the same latency, because as Andy Ross indicated, the real latency is in the hardware fifo handling. 现在,如果您想坚持使用协议并减少延迟,选择,轮询,读取将为您提供大致相同的延迟,因为正如Andy Ross所指出的那样,真正的延迟在硬件fifo处理中。

If you are lucky, you can tweak the driver behaviour without patching, but you still need to look at the driver code. 如果幸运的话,您可以调整驱动程序的行为而无需打补丁,但是您仍然需要查看驱动程序代码。 However, having the ARM handle a 10 kHz interrupt rate will certainly not be good for the overall system performance... 但是,让ARM处理10 kHz的中断速率肯定不会对整个系统性能产生好处。

Another options is to pad your packet so that you hit the fifo threshold every time. 另一种选择是填充数据包,以便每次都达到fifo阈值。 It will also confirm that if it is or not a fifo threshold problem. 它还将确认是否是fifo阈值问题。

10 msec @ 115200 is enough to transmit 100 bytes (assuming 8N1), so what you are seeing is probably because the low_latency flag is not set. 10毫秒@ 115200足以传输100个字节(假设为8N1),因此您看到的可能是因为未设置low_latency标志。 Try 尝试

setserial /dev/<tty_name> low_latency

It will set the low_latency flag, which is used y the kernel when moving data up in the tty layer : 它将设置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);
}

The schedule_work call might be responsible for the 10 msec latency you observe. schedule_work调用可能是您观察到的10毫秒延迟的原因。

Having talked to to some more engineers about the topic I came to the conclusion that this problem is not solvable in user space. 与更多的工程师讨论了该主题之后,我得出的结论是,该问题在用户空间中无法解决。 Since we need to cross the bridge into kernel land, we plan to implement an kernel module which talks our protocol and gives us latencies < 1ms. 由于我们需要跨网桥进入内核领域,因此我们计划实现一个内核模块,该模块讨论我们的协议并提供小于1ms的延迟。

--- edit --- -编辑-

Turns out I was completely wrong. 原来我是完全错误的。 All that was necessary was to increase the kernel tick rate. 所需要做的只是增加内核滴答率。 The default 100 ticks added the 10ms delay. 默认的100个滴答声增加了10ms的延迟。 1000Hz and a negative nice value for the serial process gives me the time behavior I wanted to reach. 1000Hz和串行过程的负整数值给了我想要达到的时间特性。

Serial ports on linux are "wrapped" into unix-style terminal constructs, which hits you with 1 tick lag, ie 10ms. linux上的串行端口被“包装”为unix样式的终端结构,这给您带来1滴答的延迟,即10毫秒。 Try if stty -F /dev/ttySx raw low_latency helps, no guarantees though. 尝试如果stty -F /dev/ttySx raw low_latency帮助,尽管不能保证。

On a PC, you can go hardcore and talk to standard serial ports directly, issue setserial /dev/ttySx uart none to unbind linux driver from serial port hw and control the port via inb/outb to port registers. 在PC上,您可以硬核直接与标准串行端口通信,发出setserial /dev/ttySx uart none来从串行端口hw解除绑定Linux驱动程序,并通过inb/outb控制端口到端口寄存器。 I've tried that, it works great. 我已经尝试过了,效果很好。

The downside is you don't get interrupts when data arrives and you have to poll the register. 不利的一面是当数据到达时您不会得到中断,而必须轮询寄存器。 often. 经常。

You should be able to do same on the arm device side, may be much harder on exotic serial port hw. 您应该能够在arm设备端执行相同的操作,在异国情调的串行端口硬件上可能要困难得多。

None of those system calls have an effect on latency. 这些系统调用均不影响延迟。 If you want to read and write one byte as fast as possible from userspace, you really aren't going to do better than a simple read()/write() pair. 如果您想尽可能快地从用户空间读取和写入一个字节,那么您确实比简单的read()/write()对要好。 Try replacing the serial stream with a socket from another userspace process and see if the latencies improve. 尝试用另一个用户空间进程中的套接字替换串行流,看看延迟是否有所改善。 If they don't, then your problems are CPU speed and hardware limitations. 如果没有,那么您的问题就是CPU速度和硬件限制。

Are you sure your hardware can do this at all? 您确定您的硬件完全可以做到这一点吗? It's not uncommon to find UARTs with a buffer design that introduces many bytes worth of latency. 找到具有缓冲器设计的UART并引入许多字节延迟的情况并不少见。

Here's what setserial does to set low latency on a file descriptor of a port: 这是setserial用来在端口的文件描述符上设置低延迟的方法:

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

In short : Use a USB adapter and ASYNC_LOW_LATENCY. 简而言之 :使用USB适配器和ASYNC_LOW_LATENCY。

I've used a FT232RL based USB adapter on Modbus at 115.2 kbs. 我在Modbus上以115.2 kbs使用了基于FT232RL的USB适配器。

I get about 5 transactions (to 4 devices) in about 20 mS total with ASYNC_LOW_LATENCY. 通过ASYNC_LOW_LATENCY,我总共约20毫秒获得了约5笔交易(到4台设备)。 This includes two transactions to a slow-poke device (4 mS response time). 这包括到慢速设备的两次事务处理(响应时间为4毫秒)。

Without ASYNC_LOW_LATENCY the total time is about 60 mS. 如果没有ASYNC_LOW_LATENCY则总时间约为60 ASYNC_LOW_LATENCY

With FTDI USB adapters ASYNC_LOW_LATENCY sets the inter-character timer on the chip itself to 1 mS (instead of the default 16 mS). 使用FTDI USB适配器, ASYNC_LOW_LATENCY芯片本身的字符间计时器设置为1 mS(而不是默认的16 mS)。

I'm currently using a home-brewed USB adapter and I can set the latency for the adapter itself to whatever value I want. 我目前正在使用自制的USB适配器,并且可以将适配器本身的延迟设置为所需的任何值。 Setting it at 200 µS shaves another mS off that 20 mS. 将其设置为200 µS,可将原来的20 mS减少另外的ms。

At those line speeds you should not be seeing latencies that large, regardless of how you check for readiness. 在这些线速下,无论如何检查准备情况,您都不会看到那么大的延迟。

You need to make sure the serial port is in raw mode (so you do "noncanonical reads") and that VMIN and VTIME are set correctly. 您需要确保串行端口处于原始模式(因此您要进行“非常规读取”),并且正确设置了VMIN和VTIME。 You want to make sure that VTIME is zero so that an inter-character timer never kicks in. I would probably start with setting VMIN to 1 and tune from there. 您想确保VTIME为零,以使字符间计时器永远不会启动。我可能首先将VMIN设置为1并从那里进行调整。

The syscall overhead is nothing compared to the time on the wire, so select() vs. poll(), etc. is unlikely to make a difference. 与联机时间相比,系统调用开销没有什么用,因此select()与poll()等不太可能有所不同。

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

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