GNU/Linux >> Linux Esercitazione >  >> Linux

Perché alcune shell `read` incorporate non riescono a leggere l'intera riga dal file in `/proc`?

Il problema è che quei file /proc i file su Linux appaiono come file di testo fino a stat()/fstat() è interessato, ma non comportarti come tale.

Poiché si tratta di dati dinamici, puoi eseguire solo un read() chiamata di sistema su di loro (almeno per alcuni di essi). Facendo più di uno potresti ottenere due blocchi di due contenuti diversi, quindi invece sembra un secondo read() su di essi non restituisce nulla (ovvero la fine del file) (a meno che tu non lseek() torna all'inizio (e solo all'inizio)).

Il read l'utilità deve leggere il contenuto dei file un byte alla volta per essere sicuro di non leggere oltre il carattere di nuova riga. Questo è ciò che dash fa:

 $ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
 read(0, "1", 1)                         = 1
 read(0, "", 1)                          = 0

Alcune shell come bash avere un'ottimizzazione per evitare di dover fare così tanti read() chiamate di sistema. Per prima cosa controllano se il file è ricercabile e, in tal caso, leggono in blocchi poiché sanno che possono rimettere il cursore subito dopo la nuova riga se l'hanno letto oltre:

$ 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

Con bash , avresti ancora problemi per i file proc che sono più grandi di 128 byte e possono essere letti solo in una sola chiamata di sistema di lettura.

bash sembra anche disabilitare quell'ottimizzazione quando -d viene utilizzata l'opzione.

ksh93 porta l'ottimizzazione ancora di più al punto da diventare fasulla. read di ksh93 cerca indietro, ma ricorda i dati extra che ha letto per il prossimo read , quindi il successivo read (o uno qualsiasi dei suoi builtin che leggono dati come cat o head ) non prova nemmeno a read i dati (anche se quei dati sono stati modificati da altri comandi intermedi):

$ 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

Se sei interessato a sapere perché? è così, puoi vedere la risposta nei sorgenti del kernel qui:

    if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
            *lenp = 0;
            return 0;
    }

Fondamentalmente, cercando (*ppos not 0) non è implementato per le letture (!write )di valori sysctl che sono numeri. Ogni volta che viene eseguita una lettura da /proc/sys/fs/file-max ,la routine in questione__do_proc_doulongvec_minmax() viene richiamato dalla voce per file-max nella tabella di configurazione nello stesso file.

Altre voci, come /proc/sys/kernel/poweroff_cmd sono implementati tramiteproc_dostring() che consente le ricerche, quindi puoi eseguire dd bs=1 su di esso e leggi dalla tua shell senza problemi.

Nota che dal kernel 2.6 la maggior parte dei /proc le letture sono state implementate tramite una nuova API chiamata seq_file e questo supporta le ricerche, ad esempio leggendo /proc/stat non dovrebbe causare problemi. Il /proc/sys/ l'implementazione, come possiamo vedere, non usa questa API.


Al primo tentativo, sembra un bug nelle shell che restituiscono meno di una vera Bourne Shell o dei suoi derivati ​​(sh, bosh, ksh, heirloom).

La Bourne Shell originale cerca di leggere un blocco (64 byte) le nuove varianti della Bourne Shell leggono 128 byte, ma ricominciano a leggere se non c'è un carattere di nuova riga.

Contesto:/procfs e implementazioni simili (ad esempio il file /etc/mtab montato file virtuale) hanno contenuto dinamico e un stat() la chiamata non provoca la ricreazione del contenuto dinamico prima. Per questo motivo, la dimensione di tale file (dalla lettura fino a EOF) può differire da quanto stat() ritorna.

Dato che lo standard POSIX richiede alle utility di aspettarsi letture brevi in qualsiasi momento, software che ritiene che un read() che restituisce meno dell'ordinato quantità di byte è un'indicazione EOF sono interrotte. Un'utility implementata correttamente chiama read() una seconda volta nel caso in cui restituisca meno del previsto, finché non viene restituito uno 0. Nel caso del read builtin, sarebbe ovviamente sufficiente leggere fino a EOF o fino a un NL è visto.

Se esegui truss o un clone del traliccio, dovresti essere in grado di verificare quel comportamento errato per le shell che restituiscono solo 6 nel tuo esperimento.

In questo caso particolare, sembra trattarsi di un bug del kernel Linux, vedi:

$ 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).

Il kernel Linux restituisce 0 con il secondo read e questo è ovviamente errato.

Conclusione:le shell che tentano per prime di leggere un blocco di dati abbastanza grande non attivano questo bug del kernel di Linux.


Linux
  1. Tail legge l'intero file?

  2. Stampa l'ultima riga di un file, dalla CLI

  3. Come leggere la penultima riga in un file usando Bash?

  4. Perché select è usato in Linux

  5. Perché il fork del mio processo fa sì che il file venga letto all'infinito

Come leggere i file riga per riga in Bash

Come leggere un file riga per riga in Bash

Come rimuovere X byte dalla fine di un file di grandi dimensioni senza leggere l'intero file?

Perché l'arresto di net rpc fallisce con le giuste credenziali?

Come ottenere l'URL del file Dropbox dalla riga di comando?

Linux:grep da determinate righe alla fine del file