Linuxでのスレッドとプロセス


253

Linuxはプロセスを処理するのに非常に効率的であり、スレッドに関連する多くの問題(ロックなど)があるため、Linuxではほとんどの場合スレッドの代わりにプロセスを使用する方が良いと言う人がいます。ただし、状況によってはスレッドがかなり大きなパフォーマンスの向上をもたらす可能性があるため、私は不審です。

だから私の質問は、スレッドとプロセスの両方がかなりうまく処理できる状況に直面したときに、プロセスとスレッドのどちらを使用するべきかということです。たとえば、Webサーバーを作成している場合、プロセスまたはスレッド(またはその組み合わせ)を使用する必要がありますか?


Linux 2.4との違いはありますか?
mouviciel 2009

3
Linux 2.4でのプロセスとスレッドの違いは、スレッドが通常は共有しないプロセスよりも、状態の多くの部分(アドレス空間、ファイルハンドルなど)を共有することです。Linux 2.6のNPTLは、win32およびSolarisの「プロセス」に少し似た「スレッドグループ」を与えることで、これを少し明確にします。
MarkR 2009

6
並行プログラミングは困難です。非常に高いパフォーマンスが必要でない限り、トレードオフで最も重要な側面は、デバッグ難しさです。プロセスは、この点ではるかに簡単なソリューションになります。これは、すべての通信が明示的であるためです(チェック、記録などが簡単)。対照的に、スレッドの共有メモリは、あるスレッドが別のスレッドに誤って影響を与える可能性のある膨大な数の場所を作成します。
Lutz Prechelt

1
@LutzPrechelt-並行プログラミングは、マルチスレッドおよびマルチプロセスにすることができます。並行プログラミングがマルチスレッドのみであると想定している理由はわかりません。特定の言語の制限が原因である可能性がありますが、一般的には両方の可能性があります。
iankit

2
私がリンクするLutzは、プロセスとスレッドのどちらを選択しても、並行プログラミングは難しいと述べましたが、プロセスを使用した並行プログラミングは、多くの場合、デバッグを容易にします。
user2692263

回答:


322

Linuxは1-1のスレッドモデルを使用し、(カーネルに対して)プロセスとスレッドを区別しません。すべてが実行可能なタスクです。*

Linuxでは、システムコール cloneは設定可能な共有レベルでタスクを複製します。

  • CLONE_FILES:(コピーを作成する代わりに)同じファイル記述子テーブルを共有する
  • CLONE_PARENT:新しいタスクと古いタスクの間に親子関係を設定しないでください(そうでない場合、子の getppid() =親のgetpid()
  • CLONE_VM:同じメモリ空間を共有する(COWを作成する代わりに)コピー

fork()通話clone(以上の共有)pthread_create()呼び出しclone(最も共有を)。**

forkINGのコストは pthread_createテーブルをコピーしてメモリのCOWマッピングを作成するため、 INGますが、Linuxカーネル開発者はこれらのコストを最小限に抑えることを試みました(そして成功しました)。

データが既にキャッシュに読み込まれている可能性があるため、同じメモリ空間とさまざまなテーブルを共有しているタスクを切り替えると、共有されていない場合よりも少し安価になります。ただし、何も共有されていなくても、タスクの切り替えは非常に高速です。これは、Linuxカーネルの開発者が確認しようとする(そして確認に成功する)別のことです。

実際、マルチプロセッサシステムを使用している場合は、共有を行わない方が実際にはパフォーマンスにメリットがあります。各タスクが別のプロセッサで実行されている場合、共有メモリの同期にはコストがかかります。


*簡略化。 CLONE_THREADシグナル配信を共有させます(CLONE_SIGHANDシグナルハンドラテーブルを共有する必要があります)。

**簡略化。SYS_forkSYS_clonesyscallの両方が存在しますが、カーネルでは、sys_forksys_cloneはどちらも同じdo_fork関数の非常に薄いラッパーcopy_processです。それ自体がの薄いラッパーです。はい、用語はprocessthread、およびtaskLinuxカーネルではなく、交換可能に使用されます...


6
1点足りないと思います。Webサーバーに複数のプロセスを作成する場合は、ソケットを開いて「work」を異なるスレッドに渡すために、別のプロセスを作成する必要があります。スレッディングは、単一のプロセス、複数のスレッド、クリーンなデザインを提供します。多くの場合、スレッドは自然なものであり、他の状況では、新しいプロセスは自然なものです。問題が灰色の領域に当てはまる場合、ephemientによって説明される他のトレードオフが重要になります。
Saurabh

26
@Saurabhそうではない。あなたは簡単にできsocketbindlistenfork、、その後、複数のプロセスを持つaccept同じリスニングソケットに接続します。プロセスがビジーの場合、プロセスは受け入れを停止し、カーネルは着信接続を別のプロセスにルーティングします(誰もリッスンしていない場合、カーネルはlistenバックログに応じてキューまたはドロップします)。あなたはそれ以上に仕事の配分を制御することはできませんが、通常はそれで十分です!
ephemient 2012年

2
@Bloodcount Linuxのすべてのプロセス/スレッドは、既存のプロセス/スレッドを複製する同じメカニズムによって作成されます。clone()共有されるリソースを決定するために渡されるフラグ。タスクはunshare()、後の時点でリソースを割り当てることもできます。
ephemient 2014年

4
@KarthikBalaguruカーネル自体の中に、task_struct各タスクのがあります。これはカーネルコード全体で「プロセス」と呼ばれることがよくありますが、実行可能な各スレッドに対応しています。ありませんprocess_struct; task_structsの束がthread_groupリストでリンクされている場合、それらはユーザー空間に対して同じ「プロセス」です。「スレッド」の特別な処理が少しあります。たとえば、すべての兄弟スレッドがforkとexecで停止され、「メイン」スレッドのみがに表示されls /procます。/proc/pidただし、リストされているかどうかに関係なく、すべてのスレッドにアクセスできます/proc
ephemient 2014

5
@KarthikBalaguruカーネルは、スレッドとプロセスの間の一連の動作をサポートします。たとえば、clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))作業ディレクトリ、ファイル、ロックを共有しない新しい「スレッド」を提供し、共有clone(CLONE_FILES | CLONE_FS | CLONE_IO)する「プロセス」を提供します。基盤となるシステムは、複製によってタスクを作成します。fork()そして、(私がこの答えで書いたように)別の方法pthread_create()で呼び出すライブラリ関数ですclone()
ephemient 2014

60

Linux(そして確かにUnix)には3番目のオプションがあります。

オプション1-プロセス

アプリケーションの一部(またはすべての部分)を処理するスタンドアロンの実行可能ファイルを作成し、プロセスごとに個別に起動します。たとえば、プログラムは自身のコピーを実行してタスクを委任します。

オプション2-スレッド

シングルスレッドで起動するスタンドアロンの実行可能ファイルを作成し、いくつかのタスクを実行するために追加のスレッドを作成する

オプション3-フォーク

Linux / Unixでのみ利用可能ですが、これは少し異なります。フォークされたプロセスは、実際には独自のアドレススペースを持つ独自のプロセスです。子が(通常)親または兄弟のアドレススペースに(スレッドとは異なり)影響を与えるためにできることは何もないため、堅牢性が向上します。

ただし、メモリページはコピーされず、コピーオンライトであるため、通常は想像以上に少ないメモリが使用されます。

次の2つのステップで構成されるWebサーバープログラムについて考えてみます。

  1. 構成データとランタイムデータを読み取る
  2. ページリクエストを処理する

スレッドを使用した場合、ステップ1は1回実行され、ステップ2は複数のスレッドで実行されます。「従来の」プロセスを使用した場合は、プロセスごとに手順1と2を繰り返す必要があり、構成とランタイムデータを複製して保存するためのメモリも必要になります。fork()を使用した場合は、ステップ1を1回実行してから、fork()を実行して、ランタイムデータと構成をメモリにそのまま残し、コピーせずに残すことができます。

したがって、実際には3つの選択肢があります。


7
@Qwertie forkはそれほどクールではありません。多くのライブラリが微妙な方法で壊れます(親プロセスで使用する場合)。予期しない動作を引き起こし、経験豊富なプログラマーをも混乱させます。
MarkR

2
@MarkRは、フォークを使用してライブラリを壊し、予期しない動作を作成する方法の例またはリンクを提供できますか?
Ehtesh Choudhury、2012

18
開いているmysql接続でプロセスがフォークすると、ソケットが2つのプロセス間で共有されるため、問題が発生します。1つのプロセスだけが接続を使用していても、もう1つのプロセスがその接続を閉じないようにします。
MarkR

1
fork()システムコールはPOSIXで指定されています(つまり、任意のUnixシステムで使用可能です)。基盤となるLinux API(clone()システムコール)を使用した場合、Linuxでは3つだけではなく、さらに多くの選択肢があります。 。
Lie Ryan、

2
@MarkRソケットの共有は仕様によるものです。さらに、どちらのプロセスも、ソケットでclose()を呼び出す前に、linux.die.net / man / 2 / shutdownを使用してソケットを閉じることができます。
Lelanthran、

53

それは多くの要因に依存します。プロセスはスレッドよりも重く、起動およびシャットダウンのコストが高くなります。プロセス間通信(IPC)も、スレッド間通信よりも難しく、低速です。

逆に、各プロセスは独自の仮想アドレス空間で実行されるため、プロセスはスレッドよりも安全で安全です。1つのプロセスがクラッシュしたり、バッファオーバーランが発生したりしても、他のプロセスにはまったく影響しません。一方、スレッドがクラッシュした場合は、プロセス内の他のすべてのスレッドが停止し、スレッドにバッファオーバーランが発生した場合は開きます。すべてのスレッドのセキュリティホール。

したがって、アプリケーションのモジュールがほとんど通信せずにほとんど独立して実行できる場合は、起動とシャットダウンのコストに余裕がある場合は、おそらくプロセスを使用する必要があります。IPCのパフォーマンスへの影響は最小限に抑えられ、バグやセキュリティホールに対してわずかに安全になります。多数の共有データ(複雑なデータ構造など)を取得または取得できるすべてのパフォーマンスが必要な場合は、スレッドを使用してください。


9
Adamの回答は、経営者向けのブリーフィングとして役立ちます。詳細については、MarkRとephemientが適切な説明を提供しています。例を含む非常に詳細な説明は、cs.cf.ac.uk / Dave / C / node29.htmlにありますが、部分的に日付が記載されているようです。
Cyber​​Fonic 2010年

2
Cyber​​FonicはWindowsに当てはまります。ephemientが言うように、Linuxプロセスはそれほど重くありません。Linuxでは、スレッド間の通信に使用できるすべてのメカニズム(futex、共有メモリ、パイプ、IPC)もプロセスで使用でき、同じ速度で実行されます。
ラッセルスチュアート

IPCは使いにくいですが、「共有メモリ」を使用している場合はどうでしょうか。
abhiarora

11

他の人は考慮事項について議論しました。

おそらく重要な違いは、Windowsプロセスではスレッドと比較して重くて高価であること、そしてLinuxでは違いがはるかに小さいため、方程式のバランスが異なる点で異なることです。


9

かつてUnixがあり、この古き良きUnixではプロセスに多くのオーバーヘッドがあったため、親プロセスと同じアドレス空間を共有するスレッドを作成することで、コンテキストを削減することができました。スイッチ。コンテキストスイッチをより効率的にします。

最近のLinux(2.6.x)では、スレッドと比較してプロセスのコンテキストスイッチのパフォーマンスに大きな違いはありません(スレッドに追加されるのはMMUのものだけです)。共有アドレス空間に問題があります。これは、スレッド内の障害のあるポインタが、同じアドレス空間内の親プロセスまたは別のスレッドのメモリを破壊する可能性があることを意味します。

プロセスはMMUによって保護されているため、ポインターの障害はシグナル11を発生させ、破損はありません。

私は一般にプロセスを使用しますが(Linuxではコンテキストスイッチのオーバーヘッドはあまりありませんが、MMUによるメモリ保護です)、リアルタイムスケジューラクラスが必要な場合は、pthreadを使用します。

Linuxでスレッドのパフォーマンスが大幅に向上したと思いますか?これに関するデータはありますか、それとも単なる神話ですか?


1
はい、データはあります。100,000プロセスを作成するテストと100,000スレッドを作成するテストを実行しました。スレッドバージョンの実行速度は約9倍になりました(プロセスの場合は17.38秒、スレッドの場合は1.93)。現在、これは作成時間のテストのみを行いますが、短期間のタスクの場合、作成時間は重要な場合があります。
user17918 2009年

4
@ user17918-上記のタイミングを計算するために使用したコードを共有することはできますか?
codeingfreak

大きな違いが1つあります。プロセスでは、カーネルはすべてのプロセスに対してページテーブルを作成し、広告は1つのページテーブルのみを使用するため、スレッドがプロセスよりも高速であるのが普通だと思います
c4f4t0r

もう1つの簡単な見方は、TCBはPCBよりもかなり小さいため、PCBを含むプロセスコンテキストの切り替えは、スレッドの切り替えよりも少し時間がかかることは明らかです。
Karthik Balaguru、2014

5

あなたのタスクはどの程度密に結合されていますか?

彼らが互いに独立して生活できる場合は、プロセスを使用します。それらが互いに依存している場合は、スレッドを使用します。これにより、他のタスクの操作を妨げることなく、不良プロセスを強制終了して再起動できます。


4

さらに複雑なことに、スレッドローカルストレージやUnix共有メモリなどがあります。

スレッドローカルストレージにより、各スレッドはグローバルオブジェクトの個別のインスタンスを持つことができます。RTOSで実行されるアプリケーションコード用に、linux / windowsでエミュレーション環境を構築するときだけ使用しました。RTOSでは、各タスクは独自のアドレス空間を持つプロセスであり、エミュレーション環境では、各タスクはスレッド(共有アドレス空間を持つ)でした。シングルトンのようなものにTLSを使用することで、「実際の」RTOS環境の場合と同様に、スレッドごとに個別のインスタンスを持つことができました。

共有メモリは、(明らかに)複数のプロセスが同じメモリにアクセスすることによるパフォーマンス上の利点をもたらしますが、プロセスを適切に同期させる必要があるというコスト/リスクがあります。これを行う1つの方法は、1つのプロセスで共有メモリにデータ構造を作成し、次に、従来のプロセス間通信(名前付きパイプなど)を介してその構造にハンドルを送信することです。


1
私はいくつかの統計収集にスレッドローカルストレージを使用しました。前回スレッドネットワークプログラムを作成していたとき:各スレッドは独自のカウンターに書き込み、ロックは必要ありません。メッセージが送信されたときにのみ、各スレッドは統計をグローバル合計に結合します。しかし、そうです、TLSはあまり一般的に使用されているわけではありません。一方、共有メモリ...データを効率的に送信するだけでなく、プロセス間でPOSIXセマフォを共有メモリに配置して共有することもできます。それはかなり素晴らしいです。
ephemient 2009

4

私の最近のLINUXでの作業では、ライブラリーに注意する必要があります。スレッドを使用している場合は、スレッド間で使用できるライブラリがスレッドセーフであることを確認してください。これは私を数回火傷しました。特にlibxml2はそのままではスレッドセーフではありません。スレッドセーフでコンパイルできますが、aptitudeのインストールでは得られません。


3

私はあなたが聞いていることに同意しなければなりません。クラスター(xhplなど)のベンチマークを行うと、スレッドを介したプロセスで常にパフォーマンスが大幅に向上します。</anecdote>


3

スレッド/プロセス間の決定は、それを何に使用するかによります。プロセスの利点の1つは、プロセスにPIDがあり、親を終了することなく強制終了できることです。

Webサーバーの実際の例では、apache 1.3は複数のプロセスのみをサポートしていましたが、2.0では、どちらかを切り替えられるように抽象化を追加しましコメント プロセスがより堅牢であること同意しているようですが、スレッドは少し優れたパフォーマンスを提供できます(プロセスのパフォーマンスが低下し、スレッドのみを使用したいウィンドウを除く)。


2

ほとんどの場合、スレッドよりもプロセスを優先します。スレッドは、タスクが比較的小さく(プロセスのオーバーヘッド>>分割されたタスクユニットごとにかかる時間)、それらの間でメモリを共有する必要がある場合に役立ちます。大きな配列を考えてください。また(オフトピック)、CPU使用率が100%またはそれに近い場合、マルチスレッド化または処理によるメリットはないことに注意してください。(実際には悪化します)


メリットがないとはどういう意味ですか?GUIスレッドで重い計算を実行するのはどうですか?それらを並列スレッドに移動すると、CPUの負荷に関係なく、ユーザーエクスペリエンスの点ではるかに優れたものになります。
olegst 2015

2

スレッド->スレッドはメモリ空間を共有し、CPUを抽象化したものであり、軽量です。プロセス->プロセスには独自のメモリ空間があり、コンピュータの抽象化です。タスクを並列化するには、CPUを抽象化する必要があります。ただし、スレッドよりもプロセスを使用することの利点は、スレッドがプロセスよりも使用するメモリが少なく、レイテンシが少ない一方で、セキュリティと安定性です。Webの例としては、chromeとfirefoxがあります。Chromeの場合、各タブは新しいプロセスであるため、Chromeのメモリ使用量はfirefoxより高く、提供されるセキュリティと安定性はfirefoxよりも優れています。ここでchromeによって提供されるセキュリティはより優れています。各タブは新しいプロセスであるため、異なるタブが特定のプロセスのメモリ空間に侵入することはできません。


2

みなさんはあなたの質問にうまく答えてくれたと思います。Linuxのスレッドとプロセスの詳細を追加して、カーネルのコンテキストで以前の応答のいくつかを明確にして要約します。したがって、私の回答は、Linuxのカーネル固有のコードに関するものです。Linuxカーネルのドキュメントによると、スレッドがプロセスとは異なり共有仮想アドレス空間を使用することを除いて、スレッドとプロセスの間に明確な区別はありません。また、Linuxカーネルでは、「タスク」という用語を使用して、プロセスとスレッドを総称しています。

「プロセスまたはスレッドを実装する内部構造はなく、代わりにtaskと呼ばれる抽象的なスケジューリングユニットを記述するstruct task_structがあります。」

また、Linus Torvalds氏によれば、プロセスとスレッドについてはまったく考えないでください。これは制限が多すぎるため、「アドレススペースを親から分離する」または共有アドレススペースに関して、COEまたは実行コンテキストのみが異なるためです。実際、彼はWebサーバーの例を使用して、ここでポイントを示しています(これを読むことを強くお勧めします)。

Linuxカーネルのドキュメントへの完全なクレジット


-3

リソースを共有する必要がある場合は、実際にはスレッドを使用する必要があります。

また、スレッド間のコンテキスト切り替えは、プロセス間のコンテキスト切り替えよりもはるかに安価であるという事実も考慮してください。

あなたがそうする正当な理由がない限り(セキュリティ、実績のあるパフォーマンステストなど)、個別のプロセスを明示的に使用する理由はないと思います。


3
編集する担当者はいますが、まったく同意できません。Linuxのプロセス間のコンテキストスイッチは、スレッド間のコンテキストスイッチとほぼ同じくらい安価です。
ephemient 2009
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.