如果超过 256 个字符待处理,为什么 8250 UART 驱动程序不会唤醒 TTY?

Han*_*kel 8 linux tty history console serial-console

这个 if 条件的动机是什么void serial8250_tx_chars(struct uart_8250_port *up)

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    uart_write_wakeup(port);
Run Code Online (Sandbox Code Playgroud)

它自 Linux 1.1.13(1994 年 5 月)以来一直存在,并在大多数 UART 驱动程序中重复出现。

背景:定制的Linux 3.4.91,ARMv7上的嵌入式系统,UART端口0配置为38400波特,16字节FIFO用于i/o。这些都不能在我们的设置中改变。

当的printf-ING非常沉重经由UART在控制台上,内部的4kB缓冲液(UART_XMIT_SIZE)填满,然后的用户空间进程直到缓冲器被清空(这需要在38400波特1秒!)。然后重复这种行为。这是因为函数n_tty_write()在缓冲区已满时进入休眠状态,并且由于上述可疑条件而长时间未唤醒。

如果简单地删除此检查,我会发现它更自然和有效。然后 printfs 会尽可能快地填满缓冲区,然后继续以清空缓冲区的速度,而不是我观察到的突发处理。

它在我的环境中运行良好,但肯定是我遗漏或误解了某些东西。当前的实施必须有一个原因。如果我消除这种情况,会有任何副作用吗?

作为一个附带问题:是否有配置选项可以调整此行为,例如让 printf 始终立即返回并在缓冲区已满时丢弃输出?

Jan*_*der 2

这是一种效率措施。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
Run Code Online (Sandbox Code Playgroud)

上述测试甚至不是读取和写入真实设备:整个时间差是系统在用户空间和内核空间之间跳转的频率。

如果用户空间不想被阻塞,它可以使用非阻塞 I/O,或者它可以检查使用调用select()来查看是否有空间写入设备......如果没有,它可以转储剩余部分放入其自己的缓冲区中并继续处理。不可否认,这确实使事情变得复杂,因为现在您有一个必须刷新的缓冲区...但如果您使用的是 stdio,那么通常情况下都是如此。