Neden bazı readin (yerleşik) kabukları / proc dosyasındaki tüm satırı okuyamıyor?


19

Bazı Bourne benzeri kabukları, readiçinde dosyadan bütün çizgi okuyamaz yerleşik /proc(aşağıda çalıştırılması gerekir komutu zshyerine $=shellsahip $shelldiğer kabuklu):

$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
  printf '[%s]\n' "$shell"
  $=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'       
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6

readstandart, standart girdinin bir metin dosyası olmasını gerektirir, bu gereksinim çeşitli davranışlara neden olur mu?


Metin dosyasının POSIX tanımını okuyun, bazı doğrulamalar yaparım:

$ od -t a </proc/sys/fs/file-max 
0000000   6   0   2   1   6   0  nl
0000007

$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max

NULİçeriğinde karakter yok /proc/sys/fs/file-maxve findnormal bir dosya olarak bildirildi (Bu bir hata findmı?).

Kabuk başlık altında bir şey yaptı sanırım file:

$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty

Yanıtlar:


31

Sorun şu ki /proc, Linux'taki bu dosyalar stat()/fstat(), ilgili olduğu kadarıyla metin dosyaları olarak görünürler , ancak bu şekilde davranmazlar.

Dinamik veriler olduğundan, bunlara yalnızca bir read()sistem çağrısı yapabilirsiniz (en azından bazıları için). Birden fazla şey yapmak size iki farklı içerikten oluşan iki parça alabilir, bu yüzden read()onların üzerinde bir saniye sadece hiçbir şey döndürmez (dosya sonu anlamına gelir) ( lseek()başlangıca (ve yalnızca başlangıca geri dönmedikçe)).

Yeni readsatır karakterini geçmediğinizden emin olmak için yardımcı programın dosyaların içeriğini her seferinde bir bayt okuması gerekir. Ne dashyapar:

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

Bazı mermiler bash, bu kadar çok read()sistem çağrısı yapmaktan kaçınmak için bir optimizasyona sahiptir . Önce dosyanın aranıp aranmadığını kontrol ederler ve eğer öyleyse, parçaları okurlar, o zaman imleci satır sonundan sonra okuduktan hemen sonra imleci geri koyabileceklerini bilirler:

$ 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

Bununla birlikte bash, 128 bayttan daha büyük proc dosyaları için sorun yaşarsınız ve yalnızca bir okuma sistem çağrısında okunabilir.

bash-dseçenek kullanıldığında da bu optimizasyonu devre dışı bıraktığı görülmektedir .

ksh93optimizasyonu sahte olmak için daha da ileri götürür. ksh93 en readgeri aramak yok, ama bir sonraki için okuma ek verileri hatırlar readsonraki böylece, read(ya da benzeri veri okumak onun diğer yerleşikleri herhangi catveya head) bile çalışmaz read(bu veri tarafından değiştirilmiş olsa bile verileri arasındaki diğer komutlar):

$ 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

Ah evet, stracetemelli bir açıklamanın anlaşılması çok daha kolay!
Stephen Kitt

Teşekkürler, dinamik veriler mantıklı. Peki kabuk dinamik verilerini nasıl algılar? Eğer yaparsam cat /proc/sys/fs/file-max | ..., sorun gitti.
cuonglm

3
Kabuk bunu algılamaz. Dinamik verisi olması, aynı dosyaya procfsart arda birden fazla read(2)çağrı yapamayacağı anlamına gelir ; davranış kabuğa bağlı değildir. Kullanmak catve borulama çalışır çünkü catdosyayı yeterince büyük parçalar halinde okur; Kabuğun readyerleşik özelliği borudan her seferinde bir karakter okur.
Stephen Kitt

1
İçinde biraz kirli bir çözüm var mksh. read -N 10 a < /proc/sys/fs/file-max
Ipor Sircer

1
@IporSircer. Aslında. Etrafında benzer bir çalışma şu şekilde çalışıyor gibi görünüyor zsh: read -u0 -k10(veya kullanın sysread; $mapfile[/proc/sys/fs/file-max]bu dosyalar düzenlenemediği için çalışmıyor mmap). Her durumda, herhangi bir kabukla, her zaman yapılabilir a=$(cat /proc/sys/fs/file-max). Bazı dahil sayesinde mksh, zshve ksh93, a=$(</proc/sys/fs/file-max)aynı zamanda çalışır ve okuma yapmak için bir süreç çatal değil.
Stéphane Chazelas

9

Nedenini bilmek istiyorsanız ? böyledir, burada çekirdek kaynaklarında cevabı görebilirsiniz :

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

Temel olarak, sayı olan sysctl değerlerinin *pposokumaları ( !write) için arama ( 0 değil) uygulanmaz . Her okuma işlemi yapıldığında /proc/sys/fs/file-max, söz konusu rutin aynı dosyadaki yapılandırma tablosundaki__do_proc_doulongvec_minmax() girişten çağrılır .file-max

Arayanlara izin veren gibi diğer girişler /proc/sys/kernel/poweroff_cmduygulanır proc_dostring(), böylece dd bs=1üzerinde yapabilir ve sorunsuz bir şekilde kabuktan okuyabilirsiniz.

Çekirdek 2.6'dan beri en çok /procokunan değerin seq_file adlı yeni bir API ile uygulandığını unutmayın. ve bu desteğin, örneğin okuma işleminin /proc/statsorunlara yol gerektiğini amaçlamaktadır . /proc/sys/Uygulama, gördüğümüz gibi, bu api kullanmaz.


3

İlk denemede, gerçek bir Bourne Kabuğundan veya türevlerinin geri dönüşünden (sh, bosh, ksh, yadigarı) daha az geri dönen kabuklarda bir böcek gibi görünüyor.

Orijinal Bourne Shell 128 baytlık bir blok (64 bayt) daha yeni Bourne Shell varyantlarını okumaya çalışır, ancak yeni bir satır karakteri yoksa tekrar okumaya başlarlar.

Arka plan: / procfs ve benzeri uygulamalar (örneğin, bağlı /etc/mtabsanal dosya) dinamik içeriğe sahiptir ve bir stat()çağrı ilk olarak dinamik içeriğin yeniden oluşturulmasına neden olmaz . Bu nedenle, böyle bir dosyanın boyutu (okumadan EOF'a kadar)stat() döndürülenlerden .

POSIX standardının, yardımcı programların herhangi bir zamanda kısa okumalar beklemesini gerektirdiği göz önüne alındığında read(), sipariş edilen bayt miktarından daha az dönen bir değerin bir EOF göstergesi olduğuna inanan yazılım kırılmıştır. Doğru uygulanmış bir yardımcı program read(), 0 döndürülene kadar beklenenden daha az dönmesi durumunda ikinci kez çağırır . readYerleşik durumunda, a görünene kadar EOF veya okuyana kadar okumak elbette yeterlidir NL.

Eğer koşma trussveya makas klonu çalıştırırsanız , sadece 6denemenizde dönen mermiler için yanlış davranışı doğrulayabilmelisiniz .

Bu özel durumda, bir Linux çekirdek hatası gibi görünüyor, bkz:

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

Linux çekirdeği ikincisiyle 0 döndürür readve bu elbette yanlıştır.

Sonuç: Önce yeterince büyük miktarda veri okumaya çalışan kabuklar bu Linux çekirdek hatasını tetiklemez.


Tamam, bir Linux çekirdek hatası için yeni bir doğrulama ile cevaptan çıkıldı.
schily

Bu bir hata değil, bir özelliktir!
Guntram Blohm, Monica'yı

Bu gerçekten garip bir iddia.
schily

Belgelenmesi bir özellik olurdu. Okuma kernel.org/doc/Documentation/filesystems/proc.txt , ben davranışların hiçbir belgelere bakın. Bununla birlikte, açıkça uygulayıcı tarafından amaçlandığı gibi çalışıyor, bu yüzden bu bir hata olarak düşünülecekse, uygulamada değil tasarımda bir hata.
Charles Duffy

0

/ Proc altındaki dosyalar bazen dosya içindeki alanları ayırmak için NULL karakter kullanır. Okunanın bunu başaramayacağı anlaşılıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.