一部のシェル組み込み機能が「/proc」のファイルの全行を「読み取れない」のはなぜですか?

一部のシェル組み込み機能が「/proc」のファイルの全行を「読み取れない」のはなぜですか?

一部の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

問題は、/procLinuxでは、これらのファイルは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

を使用すると、bash128バイトを超え、単一の読み取りシステム呼び出しでのみ読み取ることができるprocファイルで問題が発生します。

bash-dこのオプションを使用すると、この最適化も無効になっているように見えます。

ksh93追加の最適化はさらに偽になります。 ksh93はread逆追跡を実行しますが、nextのために読み取った追加のデータを記憶するので、readnext(またはreadなどのデータを読み取る他の組み込み関数)は、異なるコマンド間でそのデータを試行しません(修正された場合でも)。 :catheadread

$ 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_cmdviaなどの他の項目は照会を許可するため、問題なくその項目を操作してシェルから読み取ることができます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文字を使用してファイル内のフィールドを区別します。読み取りはこの問題を処理できないようです。

関連情報