Heute fiel mir ein uralter USB-Stick in die Hände, mit 2 GB Speicherkapazität. Da ich gerade eine größere Datei kopieren musste, die etwa 429 MB groß war, kam der Stick gerade recht. Einmal mit FAT32 formatiert, weil das Zielgerät nichts anderes versteht, und die Datei schnell auf den Stick kopiert. Dachte ich.

Beim Kopieren im Datei-Manager ging es gleich los: Nach zwei Sekunden waren schon vierhundertirgendwas MB kopiert, danach ging es nicht weiter. Ein paar weitere Versuche ergaben jedes Mal leicht abweichende Werte, die nach ein paar Sekunden kopiert sein sollten. Jedes Mal blieb der Kopiervorgang dann aber stecken. Vielleicht ein Bug im Datei-Manager.

Unter Windows konnte ich die Datei dann im Windows-Explorer ohne Probleme auf den Stick kopieren. Unter Linux soll das nicht gehen? Da cp keine Fortschrittsanzeige hat, habe ich mit rsync experimentiert. Nach ein paar Sekunden sah ich das hier:

rsync --info=progress2 image.bin /run/media/tim/ECCC-B50C/
    429,265,824 100%  261.90MB/s    0:00:01 (xfr#1, to-chk=0/1)

Danach passierte nichts mehr, rsync blieb einfach stehen. Also doch kein Bug im Datei-Manager, grmpf. Ein paar investigative Schritte in die falsche Richtung später ergab die dann fällige Internet-Recherche einen ersten Hinweis: Der Linux-Kernel schreibt bei einem solchen Kopiervorgang auf ein Speichermedium nicht sofort auf das Medium, sondern speichert die Informationen zunächst im Hauptspeicher. Danach (erst) schreibt er tatsächlich.

Das passte zu dem, was ich sah: Sowohl der Datei-Manager als auch rsync waren nach wenigen Sekunden so gut wie fertig, aber dann hingen sie. Mit etwas mehr Geduld ließ ich rsync noch einmal laufen, und siehe da:

rsync --info=progress2 image.bin /run/media/tim/ECCC-B50C/
    429,265,824 100% 1012.75kB/s    0:06:53 (xfr#1, to-chk=0/1)

Mit einer wahnsinnigen Geschwindigkeit war die Datei tatsächlich kopiert worden. Warum ist das so? Anscheinend kann auf den Speichertyp, der in USB-Sticks eingesetzt wird, nur sehr langsam geschrieben werden, wenn eine große Datenmenge auf einmal geschrieben wird. Genau das passiert hier, weil der Kernel die Datei in einem Stück schreiben will. Schneller soll es gehen, wenn man die Datei in kleinen Häppchen schreibt.

Das Internet schlägt vor, eine Einstellung in der Verwaltung des virtuellen Speichers zu ändern, und zwar die “dirty_bytes”. Definiert ist das als die Datenmenge, die der Kernel maximal im Hauptspeicher zwischenlagert, bevor er sie auf den Datenträger schreibt. Auslesen kann ich die derzeitige Einstellung mit cat /proc/sys/vm/dirty_bytes. Allerdings gibt das bei mir den Wert 0 zurück. Es gibt da nämlich eine weitere Einstellung, “dirty_ratio”, die dieselbe Grenze definiert, nur in Prozent des verfügbaren Hauptspeichers. Mein System hat 16 GB Hauptspeicher, und cat /proc/sys/vm/dirty_ratio gibt den Wert 20 zurück. Mein Kernel liest also bis zu 20 Prozent des Hauptspeichers, d.h. bis zu 3,2 GB, am Stück ein.

Das ist für meinen USB-Stick wohl etwas zu viel, weil dann die gesamte Datei auf einmal geschrieben werden soll. Man kann sich schon fragen, warum der Kernel da nicht je nach Speicherart und Speichermedium differenziert. Das ist hier schon 2013 im Detail diskutiert worden, wo Linus Torvalds das so erklärt hat:

And that “up to 3.2GB of dirty memory” is just crazy. Our defaults come from the old days of less memory [..]

Ich kann diese Einstellung ändern, indem ich einen der beiden Werte, egal welchen, mit einem Eintrag in die noch nicht vorhandene Datei /etc/sysctl.conf überschreibe, z.B.:

vm.dirty_bytes = 15000000

Damit setze ich die Grenze für die Zwischenspeicherung auf ungefähr 15 MB fest, womit der Wert dirty_ratio automatisch gelöscht wird. Anschließend lade ich die Konfiguration mittels sysctl -p neu. Erneutes Kopieren zeigt eine schöne Fortschrittsanzeige, ohne jedes Hängen, und den Erfolg, nämlich eine vervierfachte Schreibgeschwindigkeit:

rsync --info=progress2 image.bin /temp
    429,265,824 100%    4.28MB/s    0:01:35 (xfr#1, to-chk=0/1)

Jetzt muss ich nur noch herausfinden, ob das irgendwelche Nebenwirkungen hat, und wie ich das unter Solus so konfiguriere, dass es einen Reboot überlebt.