
一部のBourneに似たシェルでは、組み込みコマンドはファイルの行全体をread
読み取ることができません/proc
(次のコマンドはで実行する必要があり、zsh
他の$=shell
シェルに置き換える必要があります$shell
)。
$ 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
read
標準では、標準入力がテキストファイルである必要があります。、この要件によって他の動作が発生しますか?
POSIX定義を読むテキストファイル、いくつかの確認を行いました。
$ 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
内容に文字がなく、/proc/sys/fs/file-max
一般find
ファイルとしても報告されます(これはのバグですかfind
?)。
私はシェルが次のように後ろから何かをしていると思いますfile
。
$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty
答え1
問題は、/proc
Linuxでは、これらのファイルはstat()/fstat()
テキストファイルのように見えますが、それはうまくいかないということです。
動的データであるため、read()
そのデータに対して1つのシステムコールしか実行できません(少なくとも一部のデータについては)。複数の操作を実行すると、内容が異なる2つのチャンクが発生する可能性があるため、2番目の操作は何も返さないようですread()
(ファイルの終わりを意味します)(lseek()
最初に戻らない限り(そして先頭のみ))。
ユーティリティread
は一度に1バイトずつファイルの内容を読み、改行文字のみが読み取られるようにする必要があります。これはdash
:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
一部のシェルは、あまりにも多くのシステムコールをbash
実行しないように最適化するのが好きです。read()
まず、ファイルが検索可能であることを確認し、その場合はファイルを塊として読みます。すでにファイルを読んでいる場合は、カーソルを改行文字の後ろに戻すことができることを知っています。
$ 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
を使用すると、bash
128バイトを超え、単一の読み取りシステム呼び出しでのみ読み取ることができるprocファイルで問題が発生します。
bash
-d
このオプションを使用すると、この最適化も無効になっているように見えます。
ksh93
追加の最適化はさらに偽になります。 ksh93はread
逆追跡を実行しますが、nextのために読み取った追加のデータを記憶するので、read
next(またはread
などのデータを読み取る他の組み込み関数)は、異なるコマンド間でそのデータを試行しません(修正された場合でも)。 :cat
head
read
$ 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
答え2
知りたいならなぜ?そうです。カーネルのソースコードから答えを見ることができます。ここ:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
デフォルトでは、read()は数値であるsysctl値の*ppos
照会(ゼロ以外の値)を実装しません。読み取りが完了するたびに、構成!write
項目から/proc/sys/fs/file-max
関連ルーチンが呼び出されます。__do_proc_doulongvec_minmax()
file-max
テーブル同じファイルに。
/proc/sys/kernel/poweroff_cmd
viaなどの他の項目は照会を許可するため、問題なくその項目を操作してシェルから読み取ることができますproc_dostring()
。dd bs=1
カーネル 2.6 以降、ほとんどの/proc
読み込みは次の新しい API で実装されます。
シーケンスファイル
これは検索をサポートしているため、たとえば読み取りに/proc/stat
問題が発生してはいけません。ご覧のとおり、実装/proc/sys/
はこのAPIを使用しません。
答え3
最初の試みでは、戻り値が実際のBourne Shellまたはその派生(sh、bosh、ksh、heirloom)の戻り値より小さいシェルのバグのように見えます。
元のBourne Shellはブロック(64バイト)を読み取ろうとしましたが、最新のBourne Shellバリアントは128バイトを読み取ります。ただし、改行がない場合は再読み込みを開始します。
背景: /procfs と同様の実装 (マウントされた/etc/mtab
仮想ファイルなど) には動的コンテンツがあり、stat()
呼び出しのため娯楽最初は動的コンテンツです。したがって、そのファイルのサイズ(読み取りからEOFまで)は、返されたstat()
サイズと異なる場合があります。
POSIX規格は、ユーティリティが期待することを要求することを考えると短い読書いつでも、ソフトウェアはread()
aの戻り値が次の値より小さいと見なします。すでに注文しましたEOF で示されるバイト数が破損しています。read()
戻り値が予想より小さい場合は、ゼロが返されるまで正しく実装されたユーティリティが再度呼び出されます。read
内蔵ケースの場合はもちろんまで読んでも十分です。EOF
またはNL
あなたが見るまで。
トラスクローンを実行すると、実験でのみ返されたシェルの誤った動作をtruss
確認できます。6
この特別なケースでは、Linuxカーネルのバグのようです。以下を参照してください。
$ 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カーネルを返す02番目のケースでは、read
これは確かに真実ではありません。
結論:最初に十分に大きなデータブロックを読み取ろうとするシェルは、このLinuxカーネルのバグを引き起こしません。
答え4
/ procの下のファイルは、時にはNULL文字を使用してファイル内のフィールドを区別します。読み取りはこの問題を処理できないようです。