GNU/Linux >> Znalost Linux >  >> Linux

Proč některé vestavěné shelly `read` nedokážou přečíst celý řádek ze souboru v `/proc`?

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í.


Linux
  1. Používání Disku Google z příkazového řádku systému Linux

  2. Čte Tail celý soubor?

  3. Najít N nejfrekventovanějších slov v souboru se seznamem stop slov z příkazového řádku?

  1. Proč je deskriptor souboru otevřen a přečten pouze jednou?

  2. Vytiskněte poslední řádek souboru z CLI

  3. Jak odstranit X bajtů z konce velkého souboru bez přečtení celého souboru?

  1. Proč se v Linuxu používá select

  2. Proč rozvětvení mého procesu způsobuje nekonečné čtení souboru

  3. Proč se vypnutí net rpc nezdaří se správnými přihlašovacími údaji?