簡體   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