一般的にどのスレッドが共有しますか?


20

さて、これは一般的な質問です。そして、もし誰かがそれを実装固有にしたいなら、私はUnix関連のものを好むでしょう。しかし、まず一般的に次の問題を知る必要があります。

私は単一のプロセスが複数のスレッドを持つことができると読みました。同じプロセスの複数のスレッドは、それらの間で物事を共有します。彼らが何を共有し、何を共有していないのか知りたい。プロセスがアドレス空間、スタック、ヒープ、グローバル変数、コード、データ、OSリソースで構成されていることを考慮して、スレッド間で共有されるものは何ですか?私は次の推測を持っています:

  1. グローバル変数-スレッド共有グローバル変数を読み取りました。また、JavaとC#でプログラミングしているときに、クラスレベルの変数を共有するスレッドを作成しました。したがって、スレッドはグローバル変数を共有すると信じています(ただし、高レベルのプログラミング言語の概念がそのまま低オペレーティングシステムレベルの事実に変換されるかどうかはわかりません)。

  2. ヒープ-グローバル変数はヒープに格納されるため、ヒープはスレッド間で共有されます。

  3. スタック-各スレッドは独自の実行シーケンス/コードを持つことができるため、プログラムカウンターの内容をプッシュ/ポップする独自のスタックを持っている必要があります(関数呼び出しと戻りが発生した場合)。したがって、同じプロセスのスレッドはスタックを共有しません。

今、私は次のものの共有について確信が持てない

  1. アドレス空間-アドレス空間で何が正確にカウントされるのかわからない。しかし、アドレス空間は一般的にスレッドではなくプロセスのコンテキストで使用されると思います。また、同じプロセスのすべてのスレッドは親プロセスと同じアドレス空間に存在するため、スレッドはアドレス空間を共有すると言われています。(しかし、それらは同じアドレス空間内で異なるスタックを維持しますか?)

  2. OSリソース-これは非常に実装固有であると思います。たとえば、親プロセスは、すべてではなくそのスレッドの一部に同じファイルのハンドルを選択的に与えることができます。または私は誤解しており、OSリソースはファイル以外のものを意味しますか?

  3. コード-スレッドは異なるコードを持つことができるため、コードの共有が常に当てはまるわけではありません。

  4. データ-データの下で何を考慮すべきかわからない。ただし、グローバル変数がスレッド間で共有されていることを確認してください。そして、ローカル変数が同様に共有されていないことを確認してください。

全体的に、あいまいな用語、オペレーティングシステムの書籍で行われている超一般化、およびオンラインで提供される実装固有の詳細により、かなり混乱しています。だから私は私を満足させることができるいくつかの答えを見つけようとしています。

回答:


13

一般に、各スレッドには独自のレジスタ(独自のプログラムカウンターを含む)、独自のスタックポインター、および独自のスタックがあります。他のすべては、プロセスを共有するスレッド間で共有されます。

特に、プロセスは一般に、アドレス空間、ヒープ、静的データ、コードセグメント、およびファイル記述子*を共有する一連のスレッドで構成されると見なされます。

アドレス空間は、物理メモリの特定の部分に単に論理アドレスのマッピングです。したがって、プロセス内のすべてのスレッドが同じアドレス空間を共有すると言う場合foo、グローバルスコープの変数にアクセスすると、すべてのスレッドが同じ変数を見ることになります。同様に、スレッドはすべて特定の時点でコードの異なるポイントを実行している可能性がありbar()ますが、プロセス内のすべてのスレッドの同じ関数に対応するグローバル関数を呼び出すことはすべて許可されています。

最新のオペレーティングシステムのほとんどは、共有されないグローバルスコープの変数であるスレッドローカルストレージの概念を追加しました。これの通常の使用例は、変数ですerrno。これはグローバルスコープの単一変数ですが、ほとんどの最新のオペレーティングシステムでは、各スレッドに独自のローカルコピーが与えられるため、あるスレッドでのライブラリ呼び出しのエラーが他のスレッドの動作に影響を与えることはありません。

*プロセス内のすべてのスレッド(プロセスID、シグナル処理、ファイルロックなど)によって共有される追加のプロセス状態がいくつかあります。スレッドによって共有されるプロセス状態の完全なリストについては、特定のスレッド実装のドキュメントを参照する必要があります。たとえば、pthreadsのmanページ


4

スレッドは、オペレーティングシステムとプログラミング言語の2つの観点から登場します。どちらの場合も、スレッドの属性には多少の違いがあります。

スレッドの最小限の定義は、順番に発生するものであるということです。

典型的なマシン実行モデルでは、各スレッドには独自の汎用レジスターセットと独自のプログラムカウンターがあります。マシンがスタックポインタとして特定のレジスタを設定する場合、スレッドごとに1つのコピーがあります。

オペレーティングシステムの観点から見ると、オペレーティングシステムがスレッドをサポートするために最低限必要なことは、スレッドを切り替える方法を提供することです。これは自動的に発生する可能性があります(予測マルチタスクまたはスレッドが明示的な要求を行う場合のみ)(協調マルチタスク。この場合、スレッドはファイバーと呼ばれることもあります)。たとえば、異なるグループのスレッド間のプリエンプションまたは、同じグループ/タスクのスレッド間で明示的な収量が得られます。スレッド間の切り替えには、少なくとも古いスレッドのレジスタ値を保存し、新しいスレッドのレジスタ値を復元する必要があります。

タスク(またはプロセス)を分離するマルチタスクオペレーティングシステムでは、これらの用語をOSコンテキストの同義語として扱うことができます。各タスクには独自のリソース、特にアドレススペースがありますが、オープンファイル、特権などもあります。上記のプロセスのエンティティであるオペレーティングシステムカーネルによって提供されます。通常、各タスクには少なくとも1つのスレッドがあります。コードを実行しないタスクはあまり役に立ちません。オペレーティングシステムは、同じタスクで複数のスレッドをサポートする場合としない場合があります。たとえば、元のUnixはそうではありませんでした。タスクは、スレッドを切り替えることで複数のスレッドを実行できます。これには特別な特権は必要ありません。これは「ユーザースレッド」と呼ばれます」、特にUnixコンテキストで。現在、ほとんどのUNIXシステムはカーネルスレッドを提供しています。これは、特に、異なるプロセスで同じプロセスの複数のスレッドを実行する唯一の方法だからです。

計算時間を除くほとんどのオペレーティングシステムリソースは、スレッドではなくタスクに関連付けられます。一部のオペレーティングシステム(Linuxなど)は、スタックを明示的に区切ります。この場合、各スレッドには独自のスレッドがあります。しかし、カーネルがスタックについて何も知らないOSがあります。それらは、スタックに関する限り、ヒープの一部にすぎません。カーネルは通常、各スレッドのカーネルコンテキストも管理します。これは、スレッドが現在何をしているかに関する情報を含むデータ構造です。これにより、カーネルはシステムコールでブロックされた複数のスレッドを同時に処理できます。

オペレーティングシステムに関する限り、タスクのスレッドは同じコードを実行しますが、そのコード内の異なる位置(異なるプログラムカウンター値)にあります。プログラムのコードの特定の部分が常に特定のスレッドで実行される場合とそうでない場合がありますが、通常は、任意のスレッドから呼び出すことができる一般的なコード(ユーティリティ関数など)があります。すべてのスレッドは同じデータを参照しますが、そうでない場合は異なるタスクと見なされます。特定のスレッドのみがアクセスできるデータがある場合、それは通常、オペレーティングシステムではなく、プログラミング言語の範囲内にあります。

ほとんどのプログラミング言語では、ストレージは同じプログラムのスレッド間で共有されます。これは、並行プログラミングの共有メモリモデルです。競合状態が発生する可能性があるため、複数のスレッドが同じデータにアクセスできる場合、プログラマは注意する必要があるため、非常に人気がありますが、非常にエラーが発生しやすくなります。ローカル変数でさえスレッド間で共有できることに注意してください。「ローカル変数」(通常)は、関数の1回の実行中にのみ名前が有効な変数を意味しますが、別のスレッドはその変数へのポインターを取得してアクセスできます。

また、各スレッドに独自のストレージがあり、通信チャネルを介してメッセージを送信することでスレッド間の通信が行われるプログラミング言語もあります。これは、並行プログラミングのメッセージパッシングモデルです。アーランメッセージの受け渡しに焦点を当てた主要なプログラミング言語です。その実行環境はスレッドの処理が非常に軽量であり、スレッドの作成が比較的高価な操作であり、ランタイム環境が非常に大規模をサポートできない他のほとんどのプログラミング言語とは対照的に、多くの短命スレッドで書かれたプログラムを奨励します同時にスレッドの数。Erlangのシーケンシャルサブセット(スレッド内で発生する言語の一部、特にデータ操作)は(ほとんど)純粋に機能的です。したがって、スレッドはデータを含む別のスレッドにメッセージを送信できます。どちらのスレッドも、使用中に他のスレッドによってデータが変更されることを心配する必要はありません。

一部の言語では、スレッドローカルストレージをグローバルシステムと区別するための型システムの有無にかかわらず、スレッドローカルストレージを提供することで2つのモデルをブレンドしています。スレッドローカルストレージは通常、変数名が異なるスレッドの異なるストレージの場所を指定できるようにする便利な機能です。

スレッドが何であるかを理解するのに興味深いかもしれないいくつかの(難しい)フォローアップ:

  • 複数のスレッドをサポートするためにカーネルが行う必要のある最低限のものは何ですか?
  • マルチプロセッサ環境で、あるプロセッサから別のプロセッサにスレッドを移行するには何が必要ですか?
  • オペレーティングシステムのサポートなしで、組み込みのサポートを使用せずに、お気に入りのプログラミング言語で協調型マルチスレッド(コルーチン)を実装するには何が必要ですか?(ほとんどのプログラミング言語には、単一のスレッド内にコルーチンを実装するために必要なプリミティブがないことに注意してください。)
  • 並行性はあるが(明示的な)スレッドの概念がない場合、プログラミング言語はどのように見えるでしょうか?(主な例:パイ計算

これは、私が何ヶ月か読んだ中で最も興味深いものです。
JSON

2

場合によります。POSIX(およびUnixシステムが提供)やWindows(後者に精通していない、具体的に尋ねる必要がある)などによって定義されたスレッドを検討する場合、それは答えを提供します(本質的に@WanderingLogicの答えが説明するように)。Linuxには、非標準のclone(2)システムコールを使用する独自のスレッドの考え方があります。親と子が共有するものをかなりきめ細かく制御できます。これまで持つようように、それは行くfork(2)vfork(2)、本質的に内部のラッパーclone(2)、すなわち、特定のフラグとそれを呼び出し、あなたは親と何もの隣にある「スレッド」という共有を作成することができます。詳細については、マニュアルページをご覧ください。オンラインで入手できます(例:こちら)。はい、LinuxはPOSIXスタイルのスレッドを提供していますが、それ以外にも多くの機能があります。


0

スレッド共有:

  • アドレス空間
  • ヒープ
  • 静的データ
  • コードセグメント
  • ファイル記述子
  • グローバル変数
  • 子プロセス
  • 保留中のアラーム
  • シグナルとシグナルハンドラー
  • 会計情報

スレッドには独自のものがあります。

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