Masalahnya adalah /proc
itu file di Linux muncul sebagai file teks sejauh stat()/fstat()
yang bersangkutan, tapi jangan bersikap seperti itu.
Karena ini adalah data dinamis, Anda hanya dapat melakukan satu read()
sistem memanggil mereka (setidaknya untuk beberapa dari mereka). Melakukan lebih dari satu dapat memberi Anda dua bagian dari dua konten yang berbeda, jadi sepertinya read()
kedua pada mereka hanya mengembalikan apa-apa (artinya end-of-file) (kecuali jika Anda lseek()
kembali ke awal (dan ke awal saja)).
read
utilitas perlu membaca konten file satu per satu untuk memastikan tidak membaca melewati karakter baris baru. Itulah dash
melakukan:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
Beberapa shell seperti bash
memiliki pengoptimalan untuk menghindari keharusan melakukan begitu banyak read()
panggilan sistem. Mereka pertama-tama memeriksa apakah file dapat dicari, dan jika demikian, baca dalam potongan-potongan karena mereka tahu mereka dapat mengembalikan kursor tepat setelah baris baru jika mereka telah membaca melewatinya:
$ 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
Dengan bash
, Anda masih memiliki masalah untuk file proc yang berukuran lebih dari 128 byte dan hanya dapat dibaca dalam satu panggilan sistem baca.
bash
juga tampaknya menonaktifkan pengoptimalan itu saat -d
opsi digunakan.
ksh93
mengambil pengoptimalan lebih jauh sehingga menjadi palsu. read
ksh93 mencari kembali, tetapi mengingat data ekstra yang telah dibacanya untuk read
berikutnya , jadi read
berikutnya (atau salah satu bawaan lainnya yang membaca data seperti cat
atau head
) bahkan tidak mencoba read
data (meskipun data tersebut telah dimodifikasi oleh perintah lain di antaranya):
$ 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
Jika Anda tertarik untuk mengetahui mengapa? demikian, Anda dapat melihat jawabannya di sumber kernel di sini:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
Pada dasarnya, mencari (*ppos
bukan 0) tidak diterapkan untuk membaca (!write
) nilai sysctl yang berupa angka. Setiap kali pembacaan dilakukan dari /proc/sys/fs/file-max
, rutin yang dimaksud__do_proc_doulongvec_minmax()
dipanggil dari entri untuk file-max
di tabel konfigurasi di file yang sama.
Entri lainnya, seperti /proc/sys/kernel/poweroff_cmd
diimplementasikan melalui proc_dostring()
yang memungkinkan pencarian, sehingga Anda dapat melakukan dd bs=1
di atasnya dan membaca dari shell Anda tanpa masalah.
Perhatikan bahwa sejak kernel 2.6 kebanyakan /proc
membaca diimplementasikan melalui API baru yang disebut seq_file dan dukungan ini mencari jadi misalnya membaca /proc/stat
seharusnya tidak menimbulkan masalah. /proc/sys/
implementasi, seperti yang bisa kita lihat, tidak menggunakan thisapi.
Pada percobaan pertama, ini terlihat seperti bug di cangkang yang menghasilkan kurang dari Bourne Shell asli atau turunannya (sh, bosh, ksh, heirloom).
Bourne Shell asli mencoba membaca satu blok (64 byte) varian Bourne Shell yang lebih baru membaca 128 byte, tetapi mereka mulai membaca lagi jika tidak ada karakter baris baru.
Latar Belakang:/procfs dan implementasi serupa (mis. /etc/mtab
yang terpasang file virtual) memiliki konten dinamis dan stat()
panggilan tidak menyebabkan pembuatan ulang dari konten dinamis terlebih dahulu. Karena alasan ini, ukuran file tersebut (dari pembacaan hingga EOF) mungkin berbeda dari stat()
kembali.
Mengingat bahwa standar POSIX mengharuskan utilitas mengharapkan bacaan singkat kapan saja, perangkat lunak yang percaya bahwa read()
yang mengembalikan kurang dari dipesan jumlah byte adalah indikasi EOF rusak. Utilitas yang diimplementasikan dengan benar memanggil read()
kedua kalinya jika hasilnya kurang dari yang diharapkan - sampai 0 dikembalikan. Dalam hal read
bawaan, tentu saja cukup untuk membaca hingga EOF
atau sampai NL
terlihat.
Jika Anda menjalankan truss
atau klon truss, Anda harus dapat memverifikasi perilaku yang salah untuk shell yang hanya mengembalikan 6
dalam eksperimen Anda.
Dalam kasus khusus ini, sepertinya itu adalah bug kernel Linux, lihat:
$ 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).
Kernel Linux menghasilkan 0 dengan read
kedua dan ini tentu saja salah.
Kesimpulan:Shell yang pertama kali mencoba membaca potongan data yang cukup besar tidak memicu bug kernel Linux ini.