GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum lesen einige eingebaute Shells nicht die ganze Zeile aus der Datei in `/proc`?

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.


Linux
  1. N häufigste Wörter in einer Datei mit einer Stoppwortliste von der Befehlszeile aus finden?

  2. Warum wird der Dateideskriptor nur einmal geöffnet und gelesen?

  3. Drucken Sie die letzte Zeile einer Datei über die CLI

  4. Wie liest man die vorletzte Zeile in einer Datei mit Bash?

  5. Warum wird select unter Linux verwendet

So lesen Sie Dateien Zeile für Zeile in Bash

So lesen Sie eine Datei Zeile für Zeile in Bash

Wie lösche ich den Inhalt einer Datei über die Befehlszeile?

Wie erhalte ich die URL der Dropbox-Datei von der Befehlszeile?

Warum kann rsync keine Dateien von /sys in Linux kopieren?

Wie kann ich eine Datei mit Zeilennummern öffnen, die von der Befehlszeile in 'vi' angezeigt werden?