如果等待處理的字元超過 256 個,為什麼 8250 UART 驅動程式無法喚醒 TTY?

如果等待處理的字元超過 256 個,為什麼 8250 UART 驅動程式無法喚醒 TTY?

這個 if 條件的動機是什麼void serial8250_tx_chars(struct uart_8250_port *up)

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    uart_write_wakeup(port);

它從 Linux 1.1.13(1994 年 5 月)開始就存在,並且在大多數 UART 驅動程式中重複出現。

背景:客製化的Linux 3.4.91,ARMv7上的嵌入式系統,UART埠0配置為38400波特率,I/O為16位元組FIFO。在我們的設定中,這些都無法更改。

當 printf 時非常透過 UART 大量在控制台上,內部 4kB 緩衝區 ( UART_XMIT_SIZE) 填滿,然後攤位使用者空間進程,直到緩衝區被清空(在 38400 波特率下需要一秒鐘!)。然後這種行為就會重複。這是因為n_tty_write()當緩衝區已滿時函數會進入睡眠狀態,並且由於上述可疑情況而很長時間不會被喚醒。

如果簡單地刪除此檢查,我會發現它更加自然和高效。然後 printfs 將盡快填滿緩衝區,並且然後以緩衝區清空的速度繼續,而不是我正在觀察的突發處理。

它在我的環境中運作良好,但我肯定遺漏或誤解了某些東西。目前的實施必定有其原因。如果我消除這種情況會有副作用嗎?

作為一個附帶問題:是否有配置選項來調整此行為,例如讓 printf 始終立即返回並在緩衝區已滿時丟棄輸出?

答案1

這是一種效率措施。 CPU 的運行速度比串列埠快得多,如果每次緩衝區中有一點空間時核心都讓使用者空間進程運行,那麼它最終會為每個位元組的資料往返用戶空間。這非常浪費CPU時間:

$ time dd if=/dev/zero of=/dev/null bs=1 count=10000000
10000000+0 records in
10000000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 5.95145 s, 1.7 MB/s

real    0m5.954s
user    0m1.960s
sys     0m3.992s

$ time dd if=/dev/zero of=/dev/null bs=1000 count=10000
10000+0 records in
10000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 0.011041 s, 906 MB/s

real    0m0.014s
user    0m0.000s
sys     0m0.012s

上面的測試甚至不是讀取和寫入真實的設備:整個時間差是系統在用戶空間和核心空間之間跳躍的頻率。

如果用戶空間不想被阻塞,它可以使用非阻塞 I/O,或者它可以檢查使用呼叫select()來查看是否有空間寫入設備......如果沒有,它可以轉儲剩餘部分放入其自己的緩衝區中並繼續處理。不可否認,這確實使事情變得複雜,因為現在您有一個必須刷新的緩衝區...但如果您使用的是 stdio,那麼通常情況下都是如此。

相關內容