這個 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,那麼通常情況下都是如此。