Linuxのメインスタック

Linuxのメインスタック

Linuxの主なスタックは何ですか?たとえば、割り込みが発生した場合、どのスタックが使用されるのか、ユーザープロセススタックとカーネルプロセススタックの違いは何ですか?

ベストアンサー1

これはプラットフォームによって異なります。特定のプラットフォームに縛られない限り、この質問に答える方法はありません(x86-32とx86-64の違いも重要です)。ただし、x86に制限する場合は、最後のコメントに基づいていくつかの情報を提案できます。

ユーザーモードからカーネルモードまでのサービス要求(「システムコール」)には、割り込みスタイルとシステム入力スタイルの2つの主なスタイルがあります。 (この用語は説明のために私が作成したものです。)割り込みタイプ要求は、外部割り込みとまったく同じ方法でプロセッサによって処理される要求です。 x86保護モードでは、これをint 0x80(最新)またはlcall 7,0(以前の変形、SysV互換)およびいわゆる実装使用と呼びます。ドアs(作業ゲート、割り込みゲートなど)、特殊セグメント記述子で構成されています。ジョブの切り替えはプロセッサによって実行される。この遷移の間、前のジョブレジスタ(スタックポインタを含む)は古いジョブTSSに格納され、新しいジョブレジスタ(スタックポインタを含む)は新しいジョブTSSからロードされます。つまり、みんな「通常」レジスタは保存されロードされます(したがって作業時間は非常に長いです)。 (変更が延期されるFPU/SSE/などの状態に関連する別の問題があります。詳細については、マニュアルを参照してください。)

これらのサービス要求を処理するために、カーネルはブロック可能な関数呼び出し中にスレッドを切り替えることができるため、各スレッド(別名LWP - 軽量プロセス)ごとに別々のスタックを準備します。このスタックは通常、サイズが小さくなります(例:4KB)。

x86タスクスイッチが常にスタックポインタを変更した場合、カーネルのユーザーモードスタックを再利用する機会はありません。一方、これらの再利用はまったく許可されていません(少量の現在のスレッドデータを除く)。これは、ユーザープロセスページが安全ではない可能性があるためです。他のアクティブスレッドがそれを変更したりマッピングを解除したりすることもできます。そのため、ユーザー領域スタックを使用してカーネルで実行することは単に禁止されています。したがって、すべてのスレッドはその他Userland および kernelland スタックは、最新の sysenter スタイルの処理にも適用されます。 (一方、上記のように、各スレッドは他のスレッドのスタックではなく、独自のカーネルスペース用のスタックを持つ必要があります。)

Sysenter スタイルの処理ははるかに後で設計されており、SYSENTER および SYSCALL プロセッサ命令によって実装されます。システムコールがすべてのレジスタを保存する必要があるという古い(厳密な)制限を考慮せずに設計されているという点で異なります。代わりに、彼らの設計は、関数が特定のレジスタ(ほとんどのABIではこれを「一時」レジスタと呼ばれる)をランダムに変更できるようにし、いくつかのレジスタだけを変更し、古い値を維持するように注意する一般的な関数呼び出しABIに近いです。 。処理プログラムを介して取得します。 SYSENTER/SYSEXIT 命令ペア (32 ビットおよび 64 ビット) は、RDX および RCX の以前のコンテンツを破壊し (異常な方法でユーザー空間はそのコンテンツを正しい値であらかじめ埋める必要があります)、新しい RIP および RSP が MSR がロードされスタックすぐにカーネル空間に切り替わります。対照的に、SYSCALL/SYSRET(64ビットのみ)はRCXとR11を戻りアドレスとフラグとして使用します。いいえスタックを直接変更します。その後、カーネルはそのスタックの一部を使用していくつかのレジスタを保存し、それ自身のスタックに切り替えます。その理由は、1)ユーザーエリアスタックが必要なすべての値を保持するのに十分な大きさであるという保証はなく、2)セキュリティ上の理由で(上記参照)。この時点から、私たちはスレッドごとにカーネルスタックを持っています。

ユーザー空間スレッドに加えて、カーネル専用のスレッドがたくさんあります(ps出力では角かっこ内の名前で見ることができます)。これらの各スレッドには独自のスタックがあります。 1)特定のイベントまたはタイムアウト時に開始される定期的なルーチン、2)一時的なタスク、または3)実際の割り込みハンドラ要求を処理するタスクを実装します。 (ケース 3 では、古いカーネルでは「bh」、新しいカーネルでは「ksoftirqd」と命名されています。) これらのスレッドの大部分は単一の論理 CPU に接続されます。ユーザー土地がない場合、ユーザー土地スタックもありません。

AFAIK、Linuxの外部割り込みハンドラは、論理CPUごとに同時に実行される最大1つのハンドラに制限されます。これらのハンドラの実行中はIO割り込みは許可されません。 (NMIはエラー処理が容易な恐ろしい例外です。)上記と同じ理由で、ジョブ切り替え割り込みゲートを使用し、各論理CPUに対して独自のスタックを持ちます。

すでに指摘したように、これのほとんどはx86に限定されています。スタックポインタの交換を強制する作業スイッチは、他のアーキテクチャではほとんど見られません。例えば、ARM32は特権レベルごとにスタックポインタを持っているため、カーネルモードで外部割り込みが発生してもスタックポインタは変わりません。

カーネル開発の高速化により、この回答の一部の詳細は廃止される可能性があります。これは一般的な提案としてのみ検討し、閲覧する特定のバージョンについて確認してください。 x86 割り込み処理とタスク切り替えに関する追加のガイドラインについては、「インテル® 64 および IA-32 アーキテクチャーソフトウェア開発者マニュアル ボリューム 3A: システム・プログラミング・ガイド、パート 1」 (インテル Web サイトから無料で提供) を参照してください。

おすすめ記事