Was ist die Motivation dieser if-Bedingung in void serial8250_tx_chars(struct uart_8250_port *up)
?
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
Es ist seit Linux 1.1.13 (Mai 1994) vorhanden und wiederholt sich in den meisten UART-Treibern.
Hintergrund: angepasstes Linux 3.4.91, Embedded-System auf ARMv7, UART-Port 0 ist für 38400 Baud konfiguriert, 16 Byte FIFO für I/O. Nichts davon lässt sich in unserem Setup ändern.
Beim Druckensehrstark auf der Konsole über UART, der interne 4kB-Puffer ( UART_XMIT_SIZE
) füllt sich und dannVerkaufsständeder User-Space-Prozess, bis der Puffer geleert ist (was bei 38400 Baud eine Sekunde dauert!). Dann wiederholt sich dieses Verhalten. Dies liegt daran, dass die Funktion n_tty_write()
in den Ruhezustand wechselt, wenn der Puffer voll ist, und aufgrund des oben genannten fragwürdigen Zustands lange Zeit nicht aufgeweckt wird.
Ich fände es natürlicher und effizienter, wenn diese Prüfung einfach entfernt würde. Dann würde das printfs den Puffer so schnell wie möglich füllen unddann weiter mit der Geschwindigkeit, mit der der Puffer geleert wird, und nicht die Burst-Verarbeitung, die ich beobachte.
In meiner Umgebung funktioniert es einwandfrei, aber ich übersehe oder verstehe sicher etwas falsch. Es muss einen Grund für die aktuelle Implementierung geben. Gibt es irgendwelche Nebenwirkungen, wenn ich diese Bedingung entferne?
Eine Nebenfrage: Gibt es Konfigurationsmöglichkeiten, um dieses Verhalten anzupassen, zum Beispiel, dass printf immer sofort zurückkehrt und die Ausgabe verwirft, wenn der Puffer voll ist?
Antwort1
Es ist eine Effizienzmaßnahme. Die CPU läuft so viel schneller als der serielle Port, dass, wenn der Kernel den Userspace-Prozess jedes Mal laufen lassen würde, wenn im Puffer ein wenig Platz wäre, dieser am Ende für jedes einzelne Datenbyte eine Reise in den Userspace und zurück machen würde. Das ist eine große Verschwendung von CPU-Zeit:
$ 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
Beim obigen Test handelt es sich nicht einmal um ein Lesen und Schreiben von einem realen Gerät: Der ganze Zeitunterschied besteht darin, wie oft das System zwischen Benutzerbereich und Kernelbereich hin- und herspringt.
Wenn der Benutzerbereich nicht aufgehalten werden soll, kann er nicht blockierende E/A verwenden oder mit einem select()
Aufruf prüfen, ob Platz zum Schreiben auf das Gerät vorhanden ist. Wenn nicht, kann er den Rest in einen eigenen Puffer kopieren und mit der Verarbeitung fortfahren. Zugegeben, das macht die Sache komplizierter, da Sie jetzt einen Puffer haben, den Sie leeren müssen. Aber wenn Sie stdio verwenden, gilt das im Allgemeinen sowieso.