web-dev-qa-db-ja.com

Linuxでの大規模プロセスのより高速なフォーク?

fork-execveコンボと同じ効果を達成するための最新のLinuxでの最速で最良の方法は何ですか大規模なプロセスから

私の問題は、プロセスのフォークが約500Mバイトの大きさであり、単純なベンチマークテストでは、プロセスから約50フォーク/秒(最小サイズのプロセスから約1600フォーク/秒)しか達成されないため、目的のアプリケーションには遅すぎることです。

いくつかのグーグルは vfork この問題の解決策として発明されたものとして現れます...しかし それを使用しないでください についての警告もあります。最近のLinuxは、関連するcloneおよびposix_spawn呼び出しを取得したようです。これらは役立つ可能性がありますか? vforkの最新の代替品は何ですか?

私はi7で64ビットのDebianLennyを使用しています(posix_spawnが役立つ場合、プロジェクトはSqueezeに移行する可能性があります)。

31
timday

結果:ここで他の回答が示唆しているように、初期に生成されたヘルパーサブプロセスルートをたどるつもりでしたが、その後、 これ フォークのパフォーマンスを向上させるために巨大なページサポートを再利用します。

libhugetlbfs を使用して自分で試してみて、アプリのすべてのmallocに巨大なページを割り当てさせるだけで、プロセスサイズに関係なく、約2400フォーク/秒になりました(とにかく私が興味を持っている範囲を超えて)。すごい。

13
timday

Linuxでは、posix_spawn(2)を_POSIX_SPAWN_USEVFORK_フラグとともに使用して、大規模なプロセスからフォークするときにページテーブルをコピーするオーバーヘッドを回避できます。

posix_spawn(2)、その利点、およびいくつかの概要については、アプリケーションサブプロセスを作成するためのメモリ使用量の最小化を参照してください。例。

vfork(2)を利用するには、_#define _GNU_SOURCE_の前に_#include <spawn.h>_を設定してから、単にposix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK)を実行してください。

これがDebianLennyで機能し、大規模なプロセスからフォークするときに大幅なスピードアップを提供することを確認できます。

_benchmarking the various spawns over 1000 runs at 100M RSS
                            user     system      total        real
fspawn (fork/exec):     0.100000  15.460000  40.570000 ( 41.366389)
pspawn (posix_spawn):   0.010000   0.010000   0.540000 (  0.970577)
_
33
tmm1

フォークにかかる時間を実際に測定しましたか? リンクしたページ を引用します。

Linuxにはこの問題はありませんでした。 Linuxは内部でコピーオンライトセマンティクスを使用していたため、Linuxはページが変更されたときにのみページをコピーします(実際には、コピーする必要のあるテーブルがまだいくつかあります。ほとんどの場合、オーバーヘッドは重要ではありません)。

したがって、フォークのnumberは、オーバーヘッドがどれほど大きくなるかを実際には示していません。フォークによって消費される時間、および(一般的なアドバイスです)実際に実行するフォークによってのみ消費される時間を測定する必要があります。最大パフォーマンスのベンチマークではありません。

ただし、大きなプロセスのフォークが遅いことが本当にわかっている場合は、小さな補助プロセスを生成し、マスタープロセスをその入力にパイプして、そこからexecへのコマンドを受け取ることができます。小さなプロセスは、これらのコマンドをforkおよびexecします。

posix_spawn()

この関数は、私が理解している限り、デスクトップシステムではfork/execを介して実装されています。ただし、組み込みシステム(特に、オンボードに [〜#〜] mmu [〜#〜] がないシステム)では、プロセスはsyscallを介して生成され、インターフェイスはposix_spawnまたは同様の機能。 posix_spawnを説明するPOSIX標準の有益なセクション を引用します:

  • 通常、スワッピングはリアルタイム環境には遅すぎます。

  • 動的アドレス変換は、POSIXが役立つ可能性のあるすべての場所で利用できるわけではありません。

  • プロセスは、アドレス変換やその他のMMUサービスなしで実行する必要がある場合はいつでも、POSIXからオプションで選択するにはあまりにも便利です。

したがって、POSIXには、アドレス変換やその他のMMUサービスなしで効率的に実装できるプロセス作成およびファイル実行プリミティブが必要です。

時間の消費を最小限に抑えることが目標である場合、デスクトップでこの機能を利用してもメリットはないと思います。

8
P Shved

事前にサブプロセスの数がわかっている場合は、起動時にアプリケーションを事前にフォークしてから、パイプを介してexecv情報を配布するのが妥当な場合があります。あるいは、プログラムにある種の「落ち着き」がある場合は、後ですばやくターンアラウンドするために、サブプロセスを1つか2つ前もってフォークするのが合理的かもしれません。これらのオプションはどちらも問題を直接解決するものではありませんが、どちらかのアプローチがアプリに適している場合は、問題を回避できる可能性があります。

4
Rakis

私はこのブログ投稿に出くわしました: http://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/

pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg);

抜粋:

システムコールclone()が助けになります。 clone()を使用して、次の機能を持つ子プロセスを作成します。

  • 子は親と同じメモリ空間で実行されます。これは、子プロセスの作成時にメモリ構造がコピーされないことを意味します。この結果、子によって行われた非スタック変数への変更は、親プロセスによって表示されます。これはスレッドに似ているため、fork()とは完全に異なり、非常に危険です。子が親を台無しにしないようにします。
  • 子は、子が作成された直後に呼び出されているエントリ関数から開始します。これはスレッドのようなもので、fork()とは異なります。
  • 子には、threadsやfork()に似ていますが、vfork()とはまったく異なる別個のスタックスペースがあります。
  • 最も重要なこと:このスレッドのような子プロセスはexec()を呼び出すことができます。

簡単に言うと、次の方法でcloneを呼び出すことにより、スレッドに非常に似ているがexec()を呼び出すことができる子プロセスを作成します。

ただし、それでもsetuidの問題が発生する可能性があると思います。

http://ewontfix.com/7/ "setuid and vfork"

今、私たちはそれの最悪の事態に到達します。スレッドとvforkを使用すると、2つのプロセスが両方ともメモリスペースを共有し、同時に実行している状況に陥ることができます。さて、親の別のスレッドがsetuid(または他の特権に影響を与える関数)を呼び出すとどうなりますか?最終的に、共有アドレス空間で実行される特権レベルの異なる2つのプロセスが発生します。そして、これは悪いことです。

たとえば、最初はrootとして実行され、vforkで単純に実装されたposix_spawnを使用して、外部コマンドを実行するマルチスレッドサーバーデーモンについて考えてみます。このコマンドは、固定環境の固定コマンドラインであり、有害なことは何もできないため、rootとして実行するか、低い権限で実行するかは関係ありません。 (愚かな例として、プログラマーがstrftimeの使用方法を理解できなかったため、外部コマンドとして実行日を指定するとします。)

気にしないので、外部プログラムの実行と同期せずに別のスレッドでsetuidを呼び出し、通常のユーザーにドロップダウンして、ユーザー提供のコード(おそらくスクリプトまたはdlopenで取得したモジュール)をそのように実行します。ユーザー。残念ながら、実行中のposix_spawnコードの上に新しいコードをmmapするか、posix_spawnが子のexecに渡す文字列を変更する許可をユーザーに与えただけです。おっと。

3
Sam Liddicott