カーネルとユーザースレッドの間に関係はありますか?
一部のオペレーティングシステムの教科書では、「マップ 1つの(多くの)ユーザースレッドから1つの(多くの)カーネルスレッドへ」と述べています。 mapはどういう意味ですか?
マップとは、各カーネルスレッドが特定の数のユーザーモードスレッドに割り当てられることを意味します。
カーネルスレッドは、アプリケーションに特権サービス(システムコールなど)を提供するために使用されます。また、カーネルによって使用され、システム上で実行されているすべてのもの、どのプロセスにどのリソースが割り当てられているかを追跡し、それらをスケジュールします。
アプリケーションがシステムコールを頻繁に使用する場合、カーネルスレッドあたりのユーザースレッドが増え、アプリケーションの実行速度が低下します。これは、すべてのシステムコールがカーネルスレッドを通過するため、カーネルスレッドがボトルネックになるためです。
ただし、プログラムがシステムコール(または他のカーネルサービス)をほとんど使用しない場合は、オーバーヘッド以外に、パフォーマンスを大幅に低下させることなく、多数のユーザースレッドをカーネルスレッドに割り当てることができます。
カーネルスレッドの数を増やすことはできますが、これにより一般にカーネルにオーバーヘッドが追加されます。そのため、個々のスレッドはシステムコールに関して応答性が高くなりますが、システム全体が遅くなります。
そのため、カーネルスレッドの数とカーネルスレッドごとのユーザースレッドの数のバランスをとることが重要です。
http://www.informit.com/articles/printerfriendly.aspx?p=25075
ユーザー空間でのスレッドの実装
スレッドパッケージを実装するには、ユーザー空間とカーネルの2つの主な方法があります。選択はやや物議を醸し、ハイブリッド実装も可能です。ここでは、これらの方法とその利点と欠点について説明します。
最初の方法は、スレッドパッケージを完全にユーザー空間に配置することです。カーネルはそれらについて何も知りません。カーネルに関する限り、カーネルは通常のシングルスレッドプロセスを管理しています。最初の最も明白な利点は、ユーザーレベルのスレッドパッケージを、スレッドをサポートしないオペレーティングシステムに実装できることです。以前はすべてのオペレーティングシステムがこのカテゴリに分類されていましたが、現在でも一部のオペレーティングシステムは分類されています。
これらの実装はすべて、図2-8(a)に示す同じ一般的な構造を持っています。スレッドは、スレッドを管理するプロシージャのコレクションであるランタイムシステムの上で実行されます。 thread_create、thread_exit、thread_wait、およびthread_yieldのうちの4つはすでに見ていますが、通常はさらに多くあります。
スレッドがユーザー空間で管理されている場合、各プロセスには、そのプロセス内のスレッドを追跡するための専用のスレッドテーブルが必要です。このテーブルは、各スレッドのプログラムカウンター、スタックポインター、レジスター、状態などのスレッドごとのプロパティのみを追跡することを除いて、カーネルのプロセステーブルに似ています。スレッドテーブルはランタイムシステムによって管理されます。スレッドが準備完了状態またはブロック化状態に移行すると、再起動に必要な情報は、カーネルがプロセスに関する情報をプロセステーブルに格納するのとまったく同じ方法で、スレッドテーブルに格納されます。
たとえば、プロセス内の別のスレッドが作業を完了するのを待機するなど、スレッドがローカルでブロックされる原因となることがある場合、スレッドはランタイムシステムプロシージャを呼び出します。このプロシージャは、スレッドをブロック状態にする必要があるかどうかを確認します。もしそうなら、それはスレッドのレジスター(すなわち、それ自身)をスレッドテーブルに格納し、実行可能な準備ができたスレッドをテーブルで探し、マシンレジスターを新しいスレッドの保存された値で再ロードします。スタックポインターとプログラムカウンターが切り替わるとすぐに、新しいスレッドが自動的に再び有効になります。マシンにすべてのレジスタを格納する命令と、すべてのレジスタをロードする命令がある場合、スレッド切り替え全体を少数の命令で実行できます。このようにスレッドを切り替えることは、カーネルへのトラップよりも少なくとも1桁高速であり、ユーザーレベルのスレッドパッケージを支持する強力な議論です。
ただし、プロセスには1つの重要な違いがあります。たとえば、thread_yieldを呼び出す場合など、スレッドの実行が完了すると、thread_yieldのコードはスレッドの情報をスレッドテーブル自体に保存できます。さらに、スレッドスケジューラを呼び出して、実行する別のスレッドを選択できます。スレッドの状態を保存するプロシージャとスケジューラはローカルプロシージャにすぎないため、それらを呼び出す方がカーネルを呼び出すよりもはるかに効率的です。他の問題の中でも、トラップの必要性、コンテキストの切り替え、メモリキャッシュのフラッシュなどは必要ありません。これにより、スレッドのスケジューリングが非常に高速になります。
ユーザーレベルのスレッドには、他にも利点があります。これにより、各プロセスは独自のカスタマイズされたスケジューリングアルゴリズムを持つことができます。たとえば、ガベージコレクタースレッドを備えた一部のアプリケーションでは、不都合なときにスレッドが停止することを心配する必要がありません。カーネルスレッドには常にカーネル内にいくつかのテーブルスペースとスタックスペースが必要であり、非常に多数のスレッドがある場合に問題となる可能性があるため、これらもより適切にスケーリングします。
パフォーマンスは向上しますが、ユーザーレベルのスレッドパッケージにはいくつかの大きな問題があります。これらの最初の問題は、ブロッキングシステムコールの実装方法の問題です。キーが押される前に、スレッドがキーボードから読み取るとします。スレッドにシステムコールを実際に行わせると、すべてのスレッドが停止するため、受け入れられません。そもそもスレッドを作成する主な目的の1つは、スレッドごとにブロッキング呼び出しを使用できるようにする一方で、ブロックされたスレッドが他のスレッドに影響を与えないようにすることでした。システムコールをブロックすると、この目標を簡単に達成する方法を理解するのが難しくなります。
システムコールはすべて非ブロッキングになるように変更できます(たとえば、キーボードでの読み取りは、文字がバッファリングされていない場合は0バイトを返すだけです)が、オペレーティングシステムの変更を要求するのは魅力的ではありません。さらに、ユーザーレベルのスレッドの議論の1つは、既存のオペレーティングシステムで実行できるということでした。さらに、読み取りのセマンティクスを変更するには、多くのユーザープログラムを変更する必要があります。
通話がブロックされるかどうかを事前に通知できる場合は、別の方法も可能です。 UNIXの一部のバージョンでは、システムコールselectが存在します。これにより、呼び出し元は、予想される読み取りがブロックされるかどうかを確認できます。この呼び出しが存在する場合、ライブラリプロシージャの読み取りは、最初に選択呼び出しを実行し、安全な(つまりブロックしない)場合にのみ読み取り呼び出しを実行する新しいものに置き換えることができます。読み取り呼び出しがブロックされる場合、呼び出しは行われません。代わりに、別のスレッドが実行されます。次にランタイムシステムが制御を取得したときに、読み取りが安全であるかどうかを再度確認できます。このアプローチは、システムコールライブラリの一部を書き換える必要があり、非効率的で洗練されていませんが、選択肢はほとんどありません。チェックを行うためにシステムコールの周囲に配置されたコードは、ジャケットまたはラッパーと呼ばれます。
システムコールをブロックする問題に似ているのは、ページフォールトの問題です。これらはチャップで勉強します。 4.現時点では、すべてのプログラムが一度にメインメモリにあるわけではないようにコンピュータをセットアップできると言えば十分です。プログラムがメモリにない命令を呼び出すかジャンプすると、ページフォールトが発生し、オペレーティングシステムはディスクから欠落している命令(およびその隣接命令)を取得します。これはページ違反と呼ばれます。必要な命令が検索され読み込まれている間、プロセスはブロックされます。スレッドがページフォールトを引き起こした場合、カーネルはスレッドの存在を知らなくても、ディスクI/Oが完了するまでプロセス全体をブロックします。ただし、他のスレッドは実行可能である可能性があります。
ユーザーレベルのスレッドパッケージのもう1つの問題は、スレッドが実行を開始すると、最初のスレッドが自発的にCPUを放棄しない限り、そのプロセスの他のスレッドが実行されないことです。単一のプロセス内では、クロック割り込みがないため、プロセスをラウンドロビン方式で(順番に)スケジュールすることができません。スレッドが独自の自由意志のランタイムシステムに入らない限り、スケジューラーはチャンスを得ることはありません。
スレッドが永久に実行される問題の1つの考えられる解決策は、ランタイムシステムに制御を与えるために1秒に1度クロック信号(割り込み)を要求させることですが、これも粗雑でプログラムが面倒です。より高い周波数での定期的なクロック割り込みは常に可能であるとは限りません。たとえ可能であったとしても、全体のオーバーヘッドはかなりのものになる可能性があります。さらに、スレッドもクロック割り込みを必要とする場合があり、ランタイムシステムによるクロックの使用を妨げます。
ユーザーレベルのスレッドに対するもう1つの、そしておそらく最も破壊的な議論は、プログラマーは一般に、たとえばマルチスレッドWebサーバーなど、スレッドが頻繁にブロックするアプリケーションで正確にスレッドを必要とすることです。これらのスレッドは常にシステムコールを行っています。システムコールを実行するためにカーネルにトラップが発生すると、古いスレッドがブロックされている場合、カーネルがスレッドを切り替える作業はほとんど必要ありません。カーネルにこれを実行させることで、常に選択したシステムコールを実行する必要がなくなります。 readシステムコールが安全かどうかを確認します。本質的に完全にCPUにバインドされ、めったにブロックされないアプリケーションの場合、スレッドを使用する意味は何ですか?最初のn個の素数を計算したり、スレッドを使用してチェスをプレイしたりすることを真剣に提案する人はいないでしょう。
ユーザースレッドはユーザー空間で管理されます。つまり、スケジューリングや切り替えなどはカーネルからではありません。
最終的に、OSカーネルは「実行ユニット」間のコンテキスト切り替えを担当するため、ユーザースレッドは、カーネルのスケジュール可能なオブジェクト(カーネルスレッド)に関連付ける(つまり、「マップ」)必要があります。†1。
したがって、N個のユーザースレッドがある場合、N個のカーネルスレッド(1:1マップ)を使用できます。これにより、カーネルのハードウェアマルチプロセッシング(複数のCPUで実行)を利用して、かなり単純化したライブラリを作成できます。基本的に、ほとんどの作業をカーネルに任せます。ただし、カーネルスレッド関数を直接呼び出さないため、OS間でアプリを移植できます。 POSIXスレッド( PThreads )が* nixの推奨実装であり、1:1マップに従う(カーネルスレッドと実質的に同等にする)と思います。ただし、これは実装に依存するため保証されていません(PThreadを使用する主な理由は、カーネル間の移植性です)。
または、カーネルスレッドを1つだけ使用することもできます。これにより、非マルチタスクOSで実行したり、スケジューリングを完全に担当したりできます。 Windowsの ser Mode Scheduling は、このN:1マップの例です。
または、任意の数のカーネルスレッド(N:Mマップ)にマップすることもできます。 Windowsには Fibers があります。これにより、NファイバーをMカーネルスレッドにマップし、それらを協調してスケジュールすることができます。スレッドプールもこの例である可能性があります-MスレッドのN作業項目。
†1:プロセスには、実際の実行単位である少なくとも1つのカーネルスレッドがあります。また、カーネルスレッドがプロセスに含まれている必要があります。 OSは、プロセスではなく、スレッドの実行をスケジュールする必要があります。
Wikipedia と Oracle によると、ユーザーレベルのスレッドは実際にはカーネルスレッドにマウントされているレイヤーにあります。 notカーネルスレッドはと同時にユーザーレベルのスレッドを実行しますが、一般的に言えば、唯一のエンティティプロセッサ/ OSによって実際に実行されるのはカーネルスレッドです。
たとえば、2つのユーザーレベルのスレッドを持つプログラムがあり、どちらも同じカーネルスレッドにマップされている(割り当てられている)とします。時々、カーネルスレッドは最初のユーザーレベルのスレッド(そしてcurrentlyこのカーネルスレッドは最初のユーザーレベルのスレッドにマップされると言われています)を実行しますthreadは、2番目のユーザーレベルのスレッドを実行します。したがって、同じカーネルスレッドに2つのユーザーレベルのスレッドがマップされていると言います。
clarificationとして:
OSのコアはそのkernelと呼ばれるため、カーネルレベルのスレッド(つまり、カーネルが認識および管理するスレッド)が呼び出されますカーネルスレッド、サービス用のOSコアへの呼び出しは、カーネルコールと呼ばれます。 kernelthingsの間の唯一の明確な関係は、それらがOSコアに強く関連していることであり、それ以上はありません。