GNU/Linux >> Linux の 問題 >  >> Linux

一部のシェル `read` ビルトインが `/proc` のファイルから行全体を読み取れないのはなぜですか?

問題は、それらの /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 カーネル バグをトリガーしません。


Linux
  1. LinuxコマンドラインからのGoogleドライブの使用

  2. テールはファイル全体を読み取りますか?

  3. コマンドラインからストップワードリストを含むファイルで最も頻繁に使用されるN個の単語を検索しますか?

  1. ファイル記述子が開かれ、一度だけ読み取られるのはなぜですか?

  2. ファイル全体を読み取らずに、大きなファイルの末尾から X バイトを削除する方法は?

  3. Linux で select が使用される理由

  1. プロセスをフォークすると、ファイルが無限に読み取られるのはなぜですか

  2. net rpc shutdown が正しい資格情報で失敗するのはなぜですか?

  3. コマンドラインから Dropbox ファイルの URL を取得するには?