Problém je v tom, že ty /proc
soubory na Linuxu se zobrazují jako textové soubory až do stat()/fstat()
je znepokojen, ale nechovejte se tak.
Protože se jedná o dynamická data, můžete provést pouze jeden read()
systémové volání na ně (alespoň pro některé). Pokud uděláte více než jeden, můžete získat dva kusy dvou různých obsahů, takže to vypadá jako druhý read()
na nich prostě nevrací nic (což znamená konec souboru) (pokud lseek()
zpět na začátek (a pouze na začátek)).
read
obslužný program potřebuje číst obsah souborů jeden bajt po druhém, aby se ujistil, že nečte za znak nového řádku. To je to, co dash
dělá:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
Některé shelly jako bash
mít optimalizaci, abyste nemuseli dělat tolik read()
systémová volání. Nejprve zkontrolují, zda je soubor vyhledatelný, a pokud ano, čtou po částech, protože pak vědí, že mohou umístit kurzor zpět těsně za nový řádek, pokud jej přečetli:
$ 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
S bash
, budete mít stále problémy se soubory proc, které jsou větší než 128 bajtů a lze je číst pouze při jednom čtení systému.
bash
také se zdá, že deaktivuje tuto optimalizaci, když -d
volba je použita.
ksh93
posouvá optimalizaci ještě dále natolik, že se stává falešnou. read
ksh93 vyhledá zpět, ale pamatuje si přečtená data navíc pro další read
, takže další read
(nebo kteroukoli z jeho dalších vestavěných funkcí, které čtou data jako cat
nebo head
) se ani nepokouší o read
data (i když byla tato data mezi tím změněna jinými příkazy):
$ 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
Pokud vás zajímá, proč? je to tak, odpověď můžete vidět ve zdrojích jádra zde:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
V podstatě hledání (*ppos
ne 0) není implementováno pro čtení (!write
)hodnot sysctl, které jsou čísly. Kdykoli se provádí čtení z /proc/sys/fs/file-max
,dotyčná rutina__do_proc_doulongvec_minmax()
je voláno ze záznamu pro file-max
v konfigurační tabulce ve stejném souboru.
Jiné položky, například /proc/sys/kernel/poweroff_cmd
jsou implementovány pomocí proc_dostring()
což umožňuje hledání, takže můžete udělat dd bs=1
na něm a bez problémů čtěte z vašeho shellu.
Všimněte si, že od jádra 2.6 nejvíce /proc
čtení bylo implementováno přes nové API s názvem seq_file, které podporuje vyhledávání, takže např. čtení /proc/stat
by neměl způsobovat problémy. /proc/sys/
implementace, jak vidíme, nepoužívá thisapi.
Na první pokus to vypadá jako chyba ve skořápkách, které vrací méně než skutečný Bourne Shell nebo jeho deriváty (sh, bosh, ksh, heirloom).
Původní Bourne Shell se pokouší přečíst blok (64 bajtů), novější varianty Bourne Shell čtou 128 bajtů, ale začnou číst znovu, pokud není žádný nový znak řádku.
Pozadí:/procfs a podobné implementace (např. připojený /etc/mtab
virtuální soubor) mají dynamický obsah a stat()
volání nezpůsobí znovu vytvoření dynamického obsahu. Z tohoto důvodu se velikost takového souboru (od čtení po EOF) může lišit od velikosti stat()
vrátí.
Vzhledem k tomu, že standard POSIX vyžaduje, aby nástroje očekávaly krátké čtení kdykoli, software, který se domnívá, že read()
která vrátí méně než objednané počet bajtů je indikací EOF, že jsou rozbité. Správně implementovaný nástroj volá read()
podruhé v případě, že vrátí méně, než se očekávalo - dokud se nevrátí 0. V případě read
vestavěný, by samozřejmě stačilo číst až do EOF
nebo až do NL
je vidět.
Pokud spustíte truss
nebo klon krovu, měli byste být schopni ověřit nesprávné chování pro shelly, které vracejí pouze 6
ve vašem experimentu.
V tomto speciálním případě se zdá, že jde o chybu linuxového jádra, viz:
$ 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).
Linuxové jádro vrací 0 s druhým read
a to je samozřejmě nesprávné.
Závěr:Shelly, které se nejprve pokusí přečíst dostatečně velký kus dat, tuto chybu linuxového jádra nespustí.