Linuxではプロセスの処理が非常に効率的であり、スレッドに関連する問題(ロックなど)が非常に多いため、Linuxではスレッドの代わりにプロセスを使用する方がほぼ常に良いと言う人が最近聞いたことがあります。しかし、状況によってはスレッドがかなり大きなパフォーマンス向上をもたらす可能性があるため、私は疑っています。
だから私の質問は、スレッドとプロセスの両方がかなりうまく処理できる状況に直面したとき、プロセスとスレッドのどちらを使用すべきかということです。たとえば、Webサーバーを作成している場合、プロセスまたはスレッド(またはその組み合わせ)を使用する必要がありますか?
Linuxは、プロセスとスレッドを(カーネルに対して)区別せずに、1-1スレッドモデルを使用します。すべてが単なる実行可能なタスクです。 *
Linuxでは、システム呼び出しclone
は、構成可能な共有レベルでタスクを複製します。
CLONE_FILES
:(コピーを作成する代わりに)同じファイル記述子テーブルを共有しますCLONE_PARENT
:新しいタスクと古いタスクの間に親子関係を設定しない(そうでない場合、子のgetppid()
=親のgetpid()
)CLONE_VM
:( COW コピーを作成する代わりに)同じメモリ空間を共有しますfork()
はclone(
least sharing)
を呼び出し、pthread_create()
はclone(
most sharing)
を呼び出します。 **
fork
ingのコストは、テーブルのコピーとメモリのCOWマッピングの作成のためpthread_create
ingよりもわずかに高くなりますが、Linuxカーネル開発者はこれらのコストを最小限にしようと試みました(そして成功しました)。
同じメモリ空間とさまざまなテーブルを共有している場合、タスクを切り替えると、データが既にキャッシュにロードされている可能性があるため、共有していない場合よりも少し安くなります。ただし、何も共有されていなくてもタスクの切り替えは非常に高速です。これは、Linuxカーネル開発者が保証しようとする(そして保証することに成功する)他の何かです。
実際、マルチプロセッサシステムを使用している場合、not共有は実際にはパフォーマンスに有益な場合があります。各タスクが異なるプロセッサで実行されている場合、共有メモリの同期は高価です。
*簡略化。 CLONE_THREAD
は、シグナル配信を共有します(シグナルハンドラーテーブルを共有するCLONE_SIGHAND
が必要です)。
**簡略化。 SYS_fork
とSYS_clone
の両方のsyscallが存在しますが、カーネルでは、sys_fork
とsys_clone
はどちらも同じdo_fork
関数の非常に薄いラッパーです。 copy_process
の薄いラッパーです。はい、Linuxカーネルではprocess
、thread
、およびtask
という用語がかなり互換的に使用されています...
Linux(そして実際にはUnix)には3番目のオプションがあります。
アプリケーションの一部(またはすべての部分)を処理するスタンドアロンの実行可能ファイルを作成し、プロセスごとに個別に呼び出します。プログラムは自分自身のコピーを実行してタスクを委任します。
単一のスレッドで起動するスタンドアロンの実行可能ファイルを作成し、いくつかのタスクを実行するために追加のスレッドを作成します
Linux/Unixでのみ使用可能です。これは少し異なります。フォークされたプロセスは、実際には独自のアドレススペースを持つ独自のプロセスです-子が(通常)親または兄弟のアドレススペースに影響を与えることができる(スレッドとは異なります)ことはないので、堅牢性が追加されます。
ただし、メモリページはコピーされず、コピーオンライトであるため、通常は想像よりも少ないメモリが使用されます。
次の2つのステップで構成されるWebサーバープログラムを考えてみましょう。
スレッドを使用した場合、ステップ1は1回実行され、ステップ2は複数のスレッドで実行されます。 「従来の」プロセスを使用した場合、各プロセスで手順1と2を繰り返す必要があり、構成データとランタイムデータを保存するメモリを複製する必要があります。 fork()を使用した場合は、ステップ1を1回実行してから、fork()を実行して、ランタイムデータと構成をそのままコピーせずにメモリに残します。
本当に3つの選択肢があります。
それは多くの要因に依存します。プロセスはスレッドよりも重いため、起動とシャットダウンのコストが高くなります。プロセス間通信(IPC)は、スレッド間通信よりも難しく、遅いです。
逆に、各プロセスは独自の仮想アドレス空間で実行されるため、プロセスはスレッドよりも安全で安全です。 1つのプロセスがクラッシュするか、バッファオーバーランがある場合、他のプロセスにはまったく影響しませんが、スレッドがクラッシュする場合、プロセス内の他のすべてのスレッドを停止し、スレッドにバッファオーバーランがある場合は開きますすべてのスレッドにセキュリティホールがあります。
そのため、アプリケーションのモジュールがほとんど通信せずにほとんど独立して実行できる場合、起動コストとシャットダウンコストに余裕があるなら、おそらくプロセスを使用する必要があります。 IPCのパフォーマンスへの影響は最小限に抑えられ、バグやセキュリティホールに対して若干安全になります。大量の共有データ(複雑なデータ構造など)を取得または保持できるあらゆるパフォーマンスが必要な場合は、スレッドを使用してください。
他の人は考慮事項を議論しました。
おそらく重要な違いは、Windowsプロセスではスレッドに比べて重くて高価であり、Linuxでは違いがはるかに小さいため、方程式は異なる時点でバランスが取れていることです。
昔々Unixがあり、この古き良きUnixにはプロセスに多くのオーバーヘッドがあったので、賢い人々がやったことはスレッドを作成することでした。コンテキストスイッチをより効率的にするスイッチ。
現代のLinux(2.6.x)では、スレッドと比較してプロセスのコンテキストスイッチのパフォーマンスに大きな違いはありません(スレッドにはMMUのもののみが追加されます)。共有アドレススペースには問題があります。つまり、スレッド内の障害のあるポインターは、同じアドレススペース内の親プロセスまたは別のスレッドのメモリを破損する可能性があります。
プロセスはMMUによって保護されているため、障害のあるポインターはシグナル11を発生させるだけで、破損は発生しません。
私は一般的にプロセスを使用します(Linuxでのコンテキストスイッチのオーバーヘッドはそれほど多くありませんが、MMUによるメモリ保護)が、リアルタイムスケジューラクラスが必要な場合はpthreadを使用します。
Linuxでスレッドのパフォーマンスがこれほど大きく向上すると思うのはなぜですか?これに関するデータはありますか、それとも単なる神話ですか?
タスクはどの程度密結合されていますか?
それらが互いに独立して生活できる場合は、プロセスを使用します。互いに依存している場合は、スレッドを使用します。これにより、他のタスクの操作を妨げることなく、不良プロセスを強制終了して再起動できます。
さらに複雑なことに、 thread-local storage やUnix共有メモリなどがあります。
スレッドローカルストレージにより、各スレッドはグローバルオブジェクトの個別のインスタンスを持つことができます。使用したのは、RTOSで実行されるアプリケーションコード用に、Linux/Windowsでエミュレーション環境を構築するときだけでした。 RTOSでは、各タスクは独自のアドレススペースを持つプロセスでしたが、エミュレーション環境では、各タスクは(共有アドレススペースを持つ)スレッドでした。シングルトンなどにTLSを使用することにより、「実際の」RTOS環境のように、スレッドごとに個別のインスタンスを作成できました。
共有メモリは、(明らかに)複数のプロセスが同じメモリにアクセスすることでパフォーマンス上の利点を得ることができますが、プロセスを適切に同期化するコスト/リスクがあります。そのための1つの方法は、1つのプロセスで共有メモリにデータ構造を作成し、その構造に従来のプロセス間通信(名前付きパイプなど)を介してハンドルを送信することです。
私はあなたが聞いてきたことに同意する必要があります。クラスターのベンチマーク(xhpl
など)を行うと、スレッドを介したプロセスのパフォーマンスが常に大幅に向上します。 </anecdote>
私の最近のLINUXでの作業で注意すべきことの1つは、ライブラリーです。スレッドを使用している場合は、スレッド間で使用できるライブラリがスレッドセーフであることを確認してください。これは数回私を燃やしました。特に、libxml2はそのままではスレッドセーフではありません。スレッドセーフでコンパイルできますが、aptitudeインストールでは得られません。
ほとんどの場合、スレッドよりもプロセスを好むでしょう。スレッドは、タスクが比較的小さく(プロセスオーバーヘッド>>分割された各タスクユニットにかかる時間)、スレッド間でメモリを共有する必要がある場合に役立ちます。大きな配列を考えてください。また(オフトピック)、CPU使用率が100%またはそれに近い場合、マルチスレッドまたは処理によるメリットは得られないことに注意してください。 (実際には悪化します)
皆があなたの質問に応えて素晴らしい仕事をしたと思います。 Linuxのスレッドとプロセスに関する情報を追加して、カーネルのコンテキストでの以前の応答の一部を明確にし、要約しています。だから、私の応答は、Linuxのカーネル固有のコードに関するものです。 Linux Kernelのドキュメントによると、スレッドはプロセスとは異なり共有仮想アドレス空間を使用する以外は、スレッドとプロセスの明確な区別はありません。また、Linuxカーネルでは「タスク」という用語を使用して、一般にプロセスとスレッドを指します。
「プロセスまたはスレッドを実装する内部構造はありません。代わりに、taskと呼ばれる抽象スケジューリング単位を記述するstruct task_structがあります」
また、Linus Torvaldsによれば、プロセスとスレッドをまったく考えないでください。制限が厳しすぎて、唯一の違いは「アドレス空間を親から分離する」または共有アドレス空間に関してCOEまたは実行コンテキストです。実際、彼はWebサーバーの例を使用して、彼の主張 here (これを読むことを強くお勧めします)にしています。
linux kernel documentation への完全なクレジット
スレッド->スレッドはメモリ空間を共有し、CPUの抽象化であり、軽量です。プロセス->プロセスには独自のメモリ空間があり、それはコンピュータの抽象化です。タスクを並列化するには、CPUを抽象化する必要があります。ただし、スレッドよりもプロセスを使用する利点はセキュリティ、安定性です。一方、スレッドはプロセスよりも少ないメモリを使用し、待ち時間が短くなります。ウェブに関する例は、chromeおよびfirefoxです。 Chromeの場合、各タブは新しいプロセスであるため、chromeのメモリ使用量はfirefoxよりも高くなりますが、提供されるセキュリティと安定性はfirefoxよりも優れています。各タブは新しいプロセスであるため、chromeによって提供されるセキュリティはより優れています。異なるタブは特定のプロセスのメモリ空間にスヌープできません。