0

ユーザーレベルのスレッドとカーネルレベルのスレッドの違いがよくわかりません。これは私がこれまでに知っていることです:

カーネルレベルのスレッド

利点:

  • 複数のプロセッサ/コアがある場合、実際にスレッドを同時に実行できます (カーネル スケジューラがスレッドを制御するため、真の並列性)。
  • スレッドをブロックする (I/O 操作など) すべてのスレッドをブロックしない

短所:

  • コンテキストの切り替えはカーネルで行われます (つまり、ユーザー空間よりも遅い)

ユーザーレベルのスレッド

利点:

  • コンテキスト切り替えの高速化
  • スケジューリングは、アプリケーションで計画できます (OS スケジューラではありません)。

短所:

  • カーネルはスレッドについて何も知らないため、プロセスは一度に 1 つのプロセッサ/コアを使用するだけです。
  • 1 つのスレッドがブロッキング呼び出しを行うと、カーネルはスレッドの存在を認識しないため、すべてのスレッドが再びブロックされ、スケジューラは他のプロセスがプロセッサ/コアにアクセスできるようにします。実行できました。

私がこれまでに知っていることから、私はこの質問に答えることができません:

カーネルレベルのスレッドが作成されると、プロセスの .text(コード領域) はどのように変更されますか?

私の直感では、スレッドが同じアドレス空間を共有しているため、変更されないため、.text 領域は変更されません。この回答を支持するもう 1 つの理由は、.text 領域が読み取り専用であることです。さらに、他のすべての領域は同じままです (.bss、ヒープなど)。変更されるのはスタックだけです。

ただし、これが正しい答えであることを確認したいと思います。

注: 私は主に Linux カーネルのスレッドについて話します (Windows スレッドについてはあまり知りません)。

4

3 に答える 3

2

ここで質問されるスレッドには、4 つの異なるタイプがあります。

  1. バックグラウンド タスクを処理するためにカーネル自体によって作成されたスレッド。
  2. fork()syscallによって fork されたプロセス。
  3. clone()Linux syscallを使用してユーザーによって作成され、カーネルによって管理される pthread スレッド
  4. カーネル スケジューラの外部で完全に実行される「グリーン」スレッド

次の回答は、投稿で言及されている「ユーザーレベル」のスレッドが pthread の種類であり、「グリーン」の種類ではないことを前提としています。

編集: @Hristoは正しいと思います.OPは「グリーン」スレッドについて話しているか、2つを混同しています。

この回答は、で作成したスレッドについて話していることを前提としていますpthread_create()。ユーザー空間によってスケジュールされ、カーネルに認識されない「グリーン」スレッドもあります。後世のためにここに私の答えを残します。


ユーザー スレッド == グリーン スレッド

いわゆる「グリーン スレッド」は、定義上、カーネルの関与なしに VM によって完全に処理されるスレッドです。

「ユーザーレベル」のカーネルはスレッドについて何も知らないため、プロセスは一度に 1 つのプロセッサ/コアしか使用しません。

右。

1 つのスレッドがブロッキング呼び出しを行うと、カーネルはスレッドの存在を認識しないため、すべてのスレッドが再びブロックされます。

いいえ。適切なプリミティブを使用している限り、ブロッキング呼び出しによってスレッドが待機キューに入れられ、別のグリーン スレッドが実行されます。

カーネルレベルのスレッドが作成されると、プロセスの .text(コード領域) はどのように変更されますか?

グリーンスレッドでは、それらはすべて同じコード領域を使用します。

さらに、他のすべての領域は同じままです (.bss、ヒープなど)。

はい。


ユーザースレッド == pthreads

pthread_create()ユーザースレッド」では、それらと「カーネル」スレッドの間にほとんど違いはありません。clone()Linux カーネルは、システム コール を使用して、ユーザー レベルのスレッドを個別のカーネル プロセスとして実装します。clone()親プロセスと同じメモリ空間を使用する新しいプロセスを作成しますが、親メモリのコピーfork()を使用して新しいプロセスを作成します。

「ユーザーレベル」のカーネルはスレッドについて何も知らないため、プロセスは一度に 1 つのプロセッサ/コアしか使用しません。

いいえ、これは正しくありません。カーネルは、コンテキストの切り替えや、複数の CPU でさまざまなユーザーレベルのスレッドをスケジュールする機能などがあるという点で、他のプロセスと同じようにユーザーレベルのスレッドを管理します。

1 つのスレッドがブロッキング呼び出しを行うと、カーネルはスレッドの存在を認識しないため、すべてのスレッドが再びブロックされます。

いいえ、1 つのスレッドがブロックされても、他のスレッドは引き続き実行されます。繰り返しますが、これは pthread スタイルのスレッドの下にあります。

カーネルレベルのスレッドが作成されると、プロセスの .text(コード領域) はどのように変更されますか?

新しいスレッドが作成されると、そのスレッドは親のメモリ セグメンテーション テーブルを継承します。テキスト/コード ページ (およびその他の読み取り専用ページ) は読み取り専用としてマークされ、2 つのスレッドはスレッドの存続期間中メモリを共有します。すべての読み取り/書き込みページは、新しいスレッドによってそれらのセクションへの書き込みが行われるまで共有され、その時点でページは別の場所にコピーされます (コピー オン ライト)。

さらに、他のすべての領域は同じままです (.bss、ヒープなど)。

いいえ、ヒープページ、および読み取り専用としてマークされていないその他のもの (言及したスタックを含む) は、書き込まれるとすぐにコピーされます。.bss セクションについてはよくわかりませんが、実行時に初期化されるため、読み書きもできると思います。DATA は読み取り専用です。

于 2012-05-25T15:13:00.343 に答える
2

ユーザーレベルのスレッドからカーネルレベルのスレッド (LWP または軽量プロセスとも呼ばれます) に切り替えると、プログラムのテキストが変更されます。その理由は、2 種類のスレッド化がまったく異なる方法で実装/コーディングされているためです。

カーネル レベルのスレッドは、スタックとスレッド ローカル ストレージ (TLS) を除くすべてを共有する別個の命令フローです。ほとんどの場合、カーネルによって実際のプロセスのように扱われますが、相互に多くのリソースを共有するという点で軽量です。実際、Linux のシングル スレッド プロセスは、LWP が 1 つだけで、他のすべてのカーネル構造を備えたプロセスであり、完全なプロセスになっています。LWP は、通常の個別の Linux プロセスが実行されるのとほぼ同じように同時に実行されます。それらを同時に実行するために何もする必要はありません。すべてカーネル スケジューラによって実行されます。本格的なプロセスとの違いは、LWP がほとんどのデータを共有することです。それらは実行可能ファイルのセクションも共有し.textますが、実際には.textセクションは、LWP 間だけでなく、同じ実行可能ファイルのすべてのインスタンス間で共有されます。

ユーザーレベルのスレッドは、実際のマルチスレッドに似た機能を提供しますが、実際には明示的な切り替えメカニズムによって駆動されることが多いタイムシェアリング ソリューションです。これは、1 つのユーザー レベル スレッドが実行されている間、他のすべてのスレッドが保留にされることを意味します。これは、実行中の命令ストリームが 1 つしかなく、コード内のあるポイントから別のポイントにジャンプし、いくつかの CPU レジスタ (特にスタック) の保存/復元を行うためです。ポインター)。ユーザーレベルのスレッドの 1 つの形式は、libpth(GNU Portable Threads) で実装された協調マルチタスクです。ユーザー レベルのスレッドは、スレッドが同時モードと見なされるモードで実行できるようにする特別な機構をユーザー コードに追加します。libpthたとえば、各スレッドが特定の API 関数を呼び出して、他のスレッドを実行できるようにする必要があります。タイマー シグナルを使用してユーザー レベルのスレッドをプリエンプトできますが、プロセスが現在ブロッキング syscall でスタックしている場合、これは通常不可能です。そのため、ユーザーレベルのスレッドは通常、非同期 I/O やノンブロッキング操作などの高度な機能を使用して記述され、ブロッキング syscall の作成が完全に許容されるカーネルレベルのスレッドとはまったく異なります。カーネルに関する限り、これはすべて単一のカーネルレベルのスレッドで発生します。

ユーザーレベルのスレッドに似たものの最も顕著な例は、単一スレッドの実行可能ファイルで複数のネットワーク接続を並列に使用select()または処理することです。poll()この場合の「スレッド」状態は、ポーリング syscall によってアクティブとして検出されたソケット上のデータを処理する特定の通信関数への呼び出しで使用される接続状態構造です。

ユーザー レベルのスレッドをカーネル レベルのスレッドに変換するには、通常、ソース コードを変更する必要があるため (たとえば、切り替え API の呼び出しを削除する必要があります)、.textセクションも変更されます。カーネル レベルのスレッドをユーザー レベルのスレッドに変換するには、ソース コードも変更する必要があり、常に可能であるとは限りません。への呼び出しでユーザーレベルのスレッドを実装する関数の名前をプラグインすることはできませんがpthread_create()、ユーザーレベルのスレッド化の実装がタイマー信号のようなものを使用し、スレッドが特別な切り替え API を呼び出さない場合は可能かもしれません...

2 つのスレッド実装のもう 1 つの注目すべき違いは、協調スレッドでは、競合状態や共有違反 (つまり、2 つのスレッドが同じメモリ位置を同時に読み取り、変更、書き込みすること) が発生しにくいということです。一方、プリエンプティブの場合、プロセスは途中で中断される可能性があり (たとえば、協調的な場合、すべての操作はアトミックであると言えます)、そのようなアクセスは、クリティカル セクションまたは他の同期プリミティブによって保護する必要があります。

完全を期すために、Windows にはファイバーと呼ばれるユーザー レベルのスレッドの独自の実装もあります。

于 2012-05-25T17:30:14.610 に答える
0

ユーザーレベルのスレッドは実際には Linux には存在しません。pthread_create で新しいスレッドを作成すると、対応するカーネル レベルのスレッドも作成されます。これを使用すると、新しいスレッドを作成するたびstraceにシステム コールが呼び出されることは明らかです。clone()

いずれにせよ、すべてのスレッドは .text、.data、および .bss セクションを共有します。彼らが共有していない唯一のものはスタックです。(はい、はい、そしてローカル変数をスレッド化します。それには立ち入りません。)

于 2012-05-25T15:12:07.630 に答える