Das Problem ist, dass diese /proc
Dateien unter Linux erscheinen als Textdateien bis stat()/fstat()
ist besorgt, aber verhalte dich nicht so.
Da es sich um dynamische Daten handelt, können Sie nur einen read()
ausführen Systemaufruf für sie (zumindest für einige von ihnen). Wenn Sie mehr als einen machen, könnten Sie zwei Stücke mit zwei verschiedenen Inhalten erhalten, also scheint es stattdessen ein zweites read()
zu sein auf ihnen gibt einfach nichts zurück (was das Ende der Datei bedeutet) (es sei denn, Sie lseek()
zurück zum Anfang (und nur zum Anfang)).
Die read
Dienstprogramm muss den Inhalt der Dateien Byte für Byte lesen, um sicherzustellen, dass das Zeilenumbruchzeichen nicht überschritten wird. Das ist dash
tut:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
Einige Shells wie bash
haben eine Optimierung, um zu vermeiden, dass so viele read()
ausgeführt werden müssen Systemaufrufe. Sie prüfen zuerst, ob die Datei durchsuchbar ist, und wenn ja, lesen sie Teile ein, da sie dann wissen, dass sie den Cursor direkt nach dem Zeilenumbruch zurücksetzen können, wenn sie darüber hinaus gelesen haben:
$ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max
lseek(0, 0, SEEK_CUR) = 0
read(0, "1628689\n", 128) = 8
Mit bash
, hätten Sie immer noch Probleme mit proc-Dateien, die größer als 128 Bytes sind und nur in einem Read-Systemaufruf gelesen werden können.
bash
scheint diese Optimierung auch zu deaktivieren, wenn -d
Option verwendet wird.
ksh93
führt die Optimierung sogar noch weiter, bis sie falsch wird. read
von ksh93 sucht zurück, erinnert sich aber an die zusätzlichen Daten, die es für die nächsten read
gelesen hat , also die nächste read
(oder eines seiner anderen eingebauten Elemente, die Daten wie cat
lesen oder head
) versucht nicht einmal read
die Daten (auch wenn diese Daten zwischenzeitlich durch andere Befehle modifiziert wurden):
$ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 2
$ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 st
Wenn Sie wissen möchten, warum? dies ist so, Sie können die Antwort hier in den Kernel-Quellen sehen:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
Grundsätzlich sucht man nach (*ppos
nicht 0) ist für Lesevorgänge nicht implementiert (!write
) von sysctl-Werten, die Zahlen sind. Immer wenn ein Lesevorgang von /proc/sys/fs/file-max
durchgeführt wird ,die fragliche Routine__do_proc_doulongvec_minmax()
wird aus dem Eintrag für file-max
aufgerufen in der Konfigurationstabelle in derselben Datei.
Andere Einträge wie /proc/sys/kernel/poweroff_cmd
werden über proc_dostring()
implementiert was Suchvorgänge zulässt, also können Sie dd bs=1
tun drauf und lese ohne Probleme von deiner Shell.
Beachten Sie, dass seit Kernel 2.6 die meisten /proc
Lesevorgänge wurden über eine neue API namens seq_file implementiert, die Suchvorgänge unterstützt, also z. B. das Lesen von /proc/stat
sollte keine probleme machen. Der /proc/sys/
Implementierung, wie wir sehen können, verwendet thisapi nicht.
Auf den ersten Versuch sieht dies wie ein Fehler in den Shells aus, die weniger zurückgeben als eine echte Bourne-Shell oder ihre Derivate (sh, bosh, ksh, heirloom).
Die ursprüngliche Bourne-Shell versucht einen Block (64 Bytes) zu lesen, neuere Bourne-Shell-Varianten lesen 128 Bytes, aber sie beginnen erneut zu lesen, wenn kein Zeilenumbruchzeichen vorhanden ist.
Hintergrund:/procfs und ähnliche Implementierungen (z.B. das gemountete /etc/mtab
virtuelle Datei) haben dynamischen Inhalt und einen stat()
Aufruf bewirkt nicht die Neuerstellung des dynamischen Inhalts zuerst. Aus diesem Grund kann die Größe einer solchen Datei (vom Lesen bis zum EOF) von stat()
abweichen zurück.
Angesichts der Tatsache, dass der POSIX-Standard erfordert, dass Dienstprogramme kurze Lesevorgänge erwarten jederzeit Software, die glaubt, dass ein read()
die weniger zurückgibt als die bestellte Die Anzahl der Bytes ist eine EOF-Anzeige, die beschädigt sind. Ein korrekt implementiertes Dienstprogramm ruft read()
auf ein zweites Mal, falls weniger als erwartet zurückgegeben wird - bis eine 0 zurückgegeben wird. Im Fall von read
eingebaut, es würde natürlich reichen bis EOF
zu lesen oder bis NL
ist zu sehen.
Wenn Sie truss
ausführen oder ein Traversenklon, sollten Sie in der Lage sein, dieses falsche Verhalten für die Schalen zu überprüfen, die nur 6
zurückgeben in Ihrem Experiment.
In diesem speziellen Fall scheint es sich um einen Fehler im Linux-Kernel zu handeln, siehe:
$ sdd -debug bs=1 if= /proc/sys/fs/file-max
Simple copy ...
readbuf (3, 12AC000, 1) = 1
writebuf (1, 12AC000, 1)
8readbuf (3, 12AC000, 1) = 0
sdd: Read 1 records + 0 bytes (total of 1 bytes = 0.00k).
sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k).
Der Linux-Kernel gibt 0 zurück mit dem zweiten read
und das ist natürlich falsch.
Fazit:Shells, die zuerst versuchen, einen ausreichend großen Datenblock zu lesen, lösen diesen Linux-Kernel-Bug nicht aus.