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


19

いくつかのボーンのようなシェルでは、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

readstandardでは、標準入力がテキストファイルである必要がありますが、その要件はさまざまな動作を引き起こしますか?


テキストファイルの 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

回答:


31

問題は、/procLinux上のこれらのファイルstat()/fstat()が懸念される限りテキストファイルとして表示されるが、そのように動作しないことです。

それは動的データであるためread()、それらに対して1つのシステムコールしか実行できません(少なくともそれらの一部について)。複数の操作を行うと、2つの異なるコンテンツの2つのチャンクを取得できるので、代わりに、1秒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ファイルにはまだ問題があり、1回の読み取りシステムコールでしか読み取ることができません。

bashまた、この-dオプションを使用すると、その最適化が無効になるようです。

ksh93偽物になるほど最適化をさらに進めます。ksh93 readはシークバックしますが、nextのreadために読み込んだ余分なデータを覚えているので、next read(またはのようなデータを読み込む他のビルトインのいずれcathead)は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

ああ、そうです、straceベースの説明ははるかに理解しやすいです!
スティーブンキット

おかげで、動的データは理にかなっています。では、シェルは動的データをどのように検出しますか?そうすればcat /proc/sys/fs/file-max | ...、問題はなくなりました。
クオングルム

3
シェルはそれを検出しません。動的データであるという事実は、同じファイルへのprocfs複数の連続したread(2)呼び出しを処理できないことを意味します。動作はシェルに依存しません。ファイルを十分に大きなチャンクで読み取るcatため、使用およびパイピングが機能catします。シェルのread組み込みは、一度に1文字ずつパイプから読み取ります。
スティーブンキット

1
には少し汚い回避策がありmkshます。read -N 10 a < /proc/sys/fs/file-max
Iporサーサー

1
@IporSircer。確かに。同様の回避策は、次のもので動作するようzshですread -u0 -k10(または使用しsysreadます; $mapfile[/proc/sys/fs/file-max]これらのファイルはmmap編集できないため動作しません)。いずれにせよ、どんなシェルでも、いつでもすることができますa=$(cat /proc/sys/fs/file-max)。一部を含むとmkshzshそしてksh93a=$(</proc/sys/fs/file-max)また動作し、読み取りを行うためのプロセスをforkしません。
ステファンシャゼル

9

理由を知りたいならこれはそうです、あなたはここのカーネルソース答えを見ることができます

    if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
            *lenp = 0;
            return 0;
    }

基本的に、シーク(*ppos0ではなく)は!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/実装は、私たちが見ることができるように、このAPIを使用していません。


3

最初の試みでは、これは実際のBourneシェルまたはその派生物の戻り値(sh、bosh、ksh、heiroom)よりも少ないシェルのバグのように見えます。

元のBourne Shellはブロック(64バイト)を読み取ろうとしますが、新しいBourne Shellバリアントは128バイトを読み取りますが、改行文字がない場合は再び読み取りを開始します。

背景:/ procfsおよび同様の実装(マウントされた/etc/mtab仮想ファイルなど)には動的コンテンツがあり、stat()呼び出しによって動的コンテンツが最初に再作成されることはありません。このため、このようなファイルのサイズ(読み取りからEOFまで)は、stat()返されるサイズと異なる場合があります。

POSIX標準では、ユーティリティは常に短い読み取りを期待する必要があるため、バイトread()順序量よりも少ないバイトがEOF表示であると考えるソフトウェアは壊れています。正しく実装されたユーティリティはread()、0が返されるまで、予想よりも少ない値を返す場合に2回呼び出します。read組み込みの場合は、a が表示されるまで、EOF または a 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カーネルは2番目に0を返しますがread、これはもちろん正しくありません。

結論:最初に十分な量のデータを読み取ろうとするシェルは、このLinuxカーネルのバグを引き起こしません。


OK、Linuxカーネルバグの新しい検証で回答を終了しました。
気味悪い

これはバグではなく、機能です!
Guntram Blohmはモニカをサポートします

これは本当に奇妙な主張です。
気味悪い

文書化されていれば、機能になります。kernel.org/doc/Documentation/filesystems/proc.txtを読んで、動作に関するドキュメントがありません。ただし、実装者が意図したとおりに動作していることは明らかです。したがって、これをバグと見なす場合、実装ではなく設計上のバグです。
チャールズダフィー

0

/ procの下のファイルは、NULL文字を使用してファイル内のフィールドを区切ることがあります。readはこれを処理できないようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.