問題は、それらの /proc
Linux 上のファイルは stat()/fstat()
までテキスト ファイルとして表示されます 懸念していますが、そのように振る舞わないでください。
動的データなので read()
は 1 つしかできません。 それらのシステムコール(少なくともそれらのいくつかについて)。複数回実行すると、2 つの異なるコンテンツの 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 バイトを超え、1 回の読み取りシステム コールでしか読み取れない proc ファイルについては、依然として問題があります。
bash
-d
のときにその最適化を無効にするようです オプションが使用されます。
ksh93
偽物になるほど最適化をさらに進めます。 ksh93 の read
シークバックしますが、次の read
のために読み取った余分なデータを記憶しています 、次の 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
理由を知りたい場合 そうです、ここのカーネルソースで答えを見ることができます:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
基本的に、シーク (*ppos
0 以外) は読み取り用に実装されていません (!write
) ) 数値である sysctl 値。 /proc/sys/fs/file-max
から読み取りが行われるたびに 、問題のルーチン__do_proc_doulongvec_minmax()
file-max
のエントリから呼び出されます 同じファイル内の構成テーブル内。
/proc/sys/kernel/poweroff_cmd
などのその他のエントリ proc_dostring()
経由で実装されています これはシークを許可するため、 dd bs=1
を実行できます
カーネル 2.6 以降、ほとんどの /proc
に注意してください 読み取りは seq_file と呼ばれる新しい API を介して実装され、これはシークをサポートするため、たとえば /proc/stat
の読み取り 問題を引き起こしてはなりません。 /proc/sys/
ご覧のとおり、実装は thisapi を使用しません。
最初の試行では、これは実際の Bourne シェルまたはその派生物 (sh、bosh、ksh、家宝) よりも少ない値を返すシェルのバグのように見えます。
元の Bourne Shell はブロック (64 バイト) を読み取ろうとしますが、新しい Bourne Shell の亜種は 128 バイトを読み取りますが、改行文字がない場合は再度読み取りを開始します。
背景:/procfs および同様の実装 (例:マウントされた /etc/mtab
仮想ファイル) には、動的コンテンツと stat()
があります。 呼び出しは再作成を引き起こしません 最初に動的コンテンツの。このため、そのようなファイルのサイズ (読み取りから EOF まで) は、stat()
とは異なる場合があります。
POSIX 標準では、ユーティリティが短い読み取りを期待する必要があることを考えると いつでも、read()
を信じるソフトウェア 注文したよりも少ない数を返します バイト数は EOF 表示であり、壊れています。正しく実装されたユーティリティは read()
を呼び出します 2 回目は、0 が返されるまで、予想よりも少ない値を返します。 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 カーネルは 0 を返します 2 番目の read
で これはもちろん正しくありません。
結論:最初に十分な大きさのデータ チャンクを読み取ろうとするシェルは、この Linux カーネル バグをトリガーしません。