におけるこの 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 ボーでは 1 秒かかります)。その後、この動作が繰り返されます。これは、n_tty_write()
バッファがいっぱいになると関数がスリープ状態になり、上記の疑わしい条件のために長時間起動されないためです。
このチェックを単純に削除すれば、より自然で効率的だと思います。そうすれば、printfはできるだけ早くバッファを埋め、その後、バッファが空になる速度で続行します私が観察しているのはバースト処理ではなく、
私の環境では問題なく動作しますが、何かが欠けているか誤解しているに違いありません。現在の実装には理由があるはずです。その条件を削除すると、副作用はありますか?
補足的な質問ですが、この動作を調整するための設定オプションはありますか? たとえば、printf が常にすぐに戻り、バッファがいっぱいの場合は出力を破棄するようにするなどです。
答え1
これは効率化の手段です。CPU はシリアル ポートよりもはるかに高速に動作するため、カーネルがバッファに少し余裕ができるたびにユーザー空間プロセスを実行させると、データの 1 バイトごとにユーザー空間との間を往復することになります。これは 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 を使用している場合は、いずれにしても一般的には当てはまります。