这个 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波特率,16字节FIFO用于I/O。在我们的设置中,这些都无法更改。
当 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,那么通常情况下都是如此。