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.