c++ - / proc /ファイルを解析するのは安全ですか?


私は/proc/net/tcp/を解析したいのですが、それは安全ですか?

/proc/からファイルを開いて読み込み、恐れずに、他のプロセス(またはOS自体)が同じ時間にそれを変更するようにするにはどうすればよいですか?



Answers


一般的に、いいえ。 (ここでの答えのほとんどは間違っています)あなたが望むプロパティに応じて安全かもしれません。 /procファイルの一貫性についてあまりにも多く考えると、コードにバグが残ることは簡単です。 たとえば/proc/mountsが一貫したスナップショットであると仮定した場合のこのバグを参照してください。

例えば:

  • /proc/uptimeは、別の答えで言及された人のように完全にアトミックです、Linux 2.6.30 (2歳未満) 以降に限ります。 この小さなファイルであっても、それまでは競合状態にあっていましたが、大部分のエンタープライズカーネルにはまだあります。 現在のソースについてはfs/proc/uptime.cを参照してください。 2.6.30より前のカーネルでは、ファイルをopenて少しreadでから、後で再びread再度readと、最初の部分と矛盾します。 (私はちょうどこれを実証しました - あなた自身を楽しんでみてください)

  • /proc/mounts、単一のreadシステムコール内で不可分です。 したがって、すべてのファイルを一度にreadと、システム上のマウントポイントの一貫したスナップショットが取得されます。 しかし、複数のreadシステムコールを使用する場合、ファイルが大きい場合は、通常のI / Oライブラリを使用し、この問題に特別な注意を払わなければ、まさにこのことが起こります。競争状態。 一貫性のあるスナップショットは得られませんが、起動する前に存在していたマウントポイントは、表示されているものには行方不明になる可能性があります。 1つのread()に対して原子的であることを確認するにはfs/namespace.c m_start()見て、 m_start()まで保持しているマウントポイントのリストをm_stop()するセマフォを取得してください。 read()が行われます。 何がうまくいかないかを見るためには、昨年のこのバグこれは私が上にリンクしているものと同じ)を/proc/mounts mountsをblithely読む高品質のソフトウェアで見てください。

  • /proc/net/tcpは、あなたが実際に求めているものですが、これよりも一貫性がありません。 これは、表の各行内でのみアトミックです。 これを見るにはnet/ipv4/tcp_ipv4.c established_get_next()と同じファイルの直下のestablished_get_next()を見て、順番に各エントリで取るロックを見てください。 私は行ごとに一貫性の欠如を示すために便利なコードを持っていませんが、一貫性を持たせるためのロックはありません。 あなたが考えていると意味があります - ネットワーキングはしばしばシステムの非常に混雑している部分なので、この診断ツールで一貫したビューを提示することはオーバーヘッドにはなりません。

各行内に/proc/net/tcp atomicを保持するもう1つの部分は、 seq_read()バッファリングfs/seq_file.c 。これfs/seq_file.c読み取ることができます。 これにより、ある行の一部をread()で、行全体のテキストがバッファに保持されるので、次のread()は新しい行を開始する前にその行の残りの部分を取得します。 /proc/mountsでは、複数のread()呼び出しを行っても各行をアトミックに保つために同じメカニズムが使用され/proc/uptime 、新しいカーネルで/proc/uptimeがアトミックに留まるためのメカニズムです。 このメカニズムはファイル全体をバッファリングしませ 。なぜなら、カーネルはメモリ使用について慎重であるからです。

/procほとんどのファイルは、少なくとも/proc/net/tcpと一貫性があります。ほとんどのファイルは同じseq_file抽象化を使用しているため、 seq_fileは提供している情報の中で1つのエントリの一貫した画像を提供します。 しかし、 /proc/uptime例が示すように、一部のファイルは2009年のように最近seq_fileを使用するように移行されていました。 私は古いメカニズムを使用するものもあり、そのようなレベルのアトミック性も持っていないと確信しています。 これらの警告はほとんど文書化されていません。 与えられたファイルについて、唯一の保証はソースを読むことです。

/proc/net/tcp場合、それを読んで恐れずに各行を解析することができます。 しかし、一度に複数の行から結論を出そうとすると、他のプロセスやカーネル読んでいる間にそれを変更しているので、おそらくバグがあります。




/procファイルはuserspaceの通常ファイルとして表示されますが、実際のファイルではなく、userspace( openreadclose )からの標準ファイル操作をサポートするエンティティです。 これは、カーネルによって変更されている通常のファイルをディスク上に置くこととは全く異なることに注意してください。

すべてのカーネルは、 sprintfような関数を使って内部状態を自身のメモリに出力し、 read(2)システムコールを発行するたびにそのメモリをユーザスペースにコピーします。

カーネルは通常のファイルとはまったく異なる方法でこれらの呼び出しを処理します。これは、読み込んだデータのスナップショット全体がopen(2)ときに準備ができていることを意味しますopen(2)カーネルは、一貫性と原子性。 私はどこでもそれを読んだことはありませんが、そうでなければ理にかなっていません。

私のアドバイスは、あなたの特定のUnixフレーバーでprocファイルの実装を見てみることです。 これは実際には、標準によって管理されていない実装上の問題(出力の形式と内容)です。

最も単純な例は、Linuxのuptime procファイルの実装です。 single_open提供されるコールバック関数で、バッファ全体がどのように生成されるかに注意してください。




/ procは仮想ファイルシステムです:実際には、カーネルの内部構造を簡単に見ることができます。 確かにそれを読むのは間違いありません(それがここにあります)が、これらの仮想ファイルの内部は新しいバージョンのカーネルで進化するかもしれないので、長期的には危険です。

EDIT

Linuxカーネルのドキュメント 、第1.4章のprocのドキュメントにあるより多くの情報入手可能な情報が時間の経過と共にどのように進化するかについての情報が見つからない。 私はそれが開かれて凍っていると思ったが、明確な答えを得ることはできない。

EDIT2

Sco docによると(Linuxではなく、* nixのすべての味がそのように振る舞います)

プロセスの状態と結果として/ procファイルの内容は瞬間から瞬時に変わることがありますが、/ procファイルの単一のread(2)は状態の「穏やかな」表現を返すことが保証されます。プロセスの状態のアトミックスナップショット。 このような保証は、実行中のプロセスの/ procファイルに適用された連続読み取りには適用されません。 さらに、アトミック性は、as(アドレス空間)ファイルに適用されるI / Oに対して特に保証されません。 プロセスのアドレス空間の内容は、そのプロセスのLWPまたはシステム内の他のプロセスによって同時に変更される可能性があります。




Linuxカーネルのprocfs APIは、読み取りが一貫性のあるデータを返すようにするインターフェイスを提供します。 __proc_file_readのコメントを読んで__proc_file_read 。 大きなコメントブロックの項目1)は、このインターフェースを説明しています。

もちろん、このインタフェースを正しく使用して、返されるデータが一貫していることを確認するためには、特定のprocファイルの実装が必要です。 あなたの質問に答えてください:いいえ、カーネルは読み込み中のprocファイルの一貫性を保証しませんが、それらのファイルの実装が一貫性を提供する手段を提供します。




私は現在、組み込みARMターゲット上でドライバ開発を行っているので、Linux 2.6.27.8用のソースがあります。

例えば、934行目の... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cには、

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));

どの出力

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...

関数raw_sock_seq_show()は、関数を処理するprocfsの階層の一部です。 テキストは、 /proc/net/tcpファイルのread()要求が生成されるまで生成されません。procfsの読み込みは、情報の更新よりもはるかに一般的ではないため、妥当なメカニズムです。

一部のドライバ(鉱山など)は、単一のsprintf()してproc_read関数を実装しています。 コアドライバの実装における余分な複雑さは、単一の読み取り中に中間のカーネルスペースバッファに収まらない可能性のある非常に長い出力を処理することです。

私は、64Kの読み込みバッファを使用するプログラムでそれをテストしましたが、私のシステムではproc_readがデータを返すために3072バイトのカーネル空間バッファが発生します。 多くのテキストを返すためには、ポインタを前にした複数の呼び出しが必要です。 複数のI / Oが必要な場合に返されるデータを一貫性のあるものにする正しい方法がわかりません。 確かに、 /proc/net/tcp各エントリは自己一貫性があります。 行が並んでいて、異なる時にスナップショットが出る可能性はあります。




未知のバグを/procて、 /proc競合状態がなく、壊れたデータや古いデータと新しいデータが混在する可能性があります。 この意味で、それは安全です。 しかし、 /procから読み込んだデータの大部分は、生成されるとすぐに潜在的に時代遅れであり、読んだり処理するまでにはなおさら厳しい競合状態が残っています。 たとえば、プロセスはいつでも終了する可能性があり、新しいプロセスに同じPIDを割り当てることができます。 競争条件なしで使用できる唯一のプロセスIDは、自分の子プロセスです。 ネットワーク情報(オープンポートなど)にも同じですが、実際には/proc情報の大部分が入ります。 自分自身のプロセスや潜在的に子プロセスに関するデータを除いて、 /procデータが正確であることに頼るのは悪くて危険な習慣と考えています。 もちろん、 /procからの他の情報をユーザ/管理者に情報/ロギングなどのために提示することは、依然として有用かもしれません。 目的。




/ procファイルから読み込むと、カーネルはあらかじめそのprocファイルの "読み込み"機能として登録されている関数を呼び出しています。 fs / proc / generic.cの__proc_file_read関数を参照してください。

したがって、proc readの安全性は、カーネルがread要求を満たすために呼び出す関数と同じくらい安全です。 その関数がバッファに格納されたすべてのデータを適切にロックして返す場合、その関数を使用して読み取ることは完全に安全です。 / proc / net / tcpへの読み込み要求を満たすために使用されるようなprocファイルは、しばらくの間、綿密なレビューを受けているので、あなたが求めるほど安全です。 実際、多くの一般的なLinuxユーティリティは、procファイルシステムからの読み込みと出力の書式設定の方法が異なります。 (私の頭の上から離れて、私は 'ps'と 'netstat'がこれを行うと思う)。

いつものように、あなたは私の言葉を取る必要はありません。 あなたはあなたの恐怖を落とすために源を見ることができます。 proc_net_tcp.txtの次のドキュメントは、/ proc / net / tcpの "読み取り"機能がどこにあるかを示しているので、そのprocファイルから読み込んだときに実行される実際のコードを見て、ロックハザード。

このドキュメントでは、/ proc / net / tcpインタフェースと/ proc / net / tcp6インタフェースについて説明します。
これらのインタフェースはtcp_diagの方が推奨されないことに注意してください。 これらの/ procインタフェースは現在アクティブなTCP接続に関する情報を提供し、それぞれnet / ipv4 / tcp_ipv4.cのtcp4_seq_show()とnet / ipv6 / tcp_ipv6.cのtcp6_seq_show()によって実装されます。