2016-12-19 8 views
1

これは特定のOSに関する質問ではありませんが、Windowsを例にしましょう。ユーザー空間プログラムは、Windows APIを使用してkernelspaceと通信します。しかし、私はそれがいかに可能か理解していません。 MSのウェブサイトによると、APIはユーザー空間に存在します。カーネル空間にアクセスするためには、正しく理解すればカーネル空間内になければなりません。だから、Windows APIがカーネル空間に話す特別な特権を得る仕組みは何ですか?そのメカニズムはどの空間で動作しますか?現代のすべてのPC OSにこの種のものが普遍的ですか?オペレーティングシステムは、ユーザー空間プログラムがカーネル空間プログラムとやりとりすることを可能にしますか?

+1

"システムコール"を参照してください。 – glauxosdever

+0

こんにちは、私はメカニズムが*システムコールと呼ばれていることを知っています。私の質問は、オペレーティングシステムが情報を2つのメモリ空間の間でどのように転送するかについてです。システムコールはおそらく関数であり、おそらくユーザ空間で動作するプログラムにコンパイルされます。それはカーネル空間にアクセスすべきではありません。それはなぜですか?例えば中間の3番目のスペースはありますか? –

+0

@MichaelStachowsky質問があればお気軽に。 –

答えて

1

CPUは、ユーザメモリ空間(ユーザモードでアクセス可能)と保護されたメモリ空間(カーネルモードでアクセス可能)との間の情報転送の中間としてCPUレジスタを介して動作します。ここで

は例です:

ユーザーは、より高いレベルの言語でプログラムを書きとします。プログラムの実行が行われると、CPUは仮想アドレスを生成します。

読み書き操作が行われる前に、仮想アドレスは物理アドレスに変換されます。変換機構(メモリ管理ユニット)はカーネルモードでのみアクセス可能であり、保護されたメモリに格納されるため、変換はカーネルモードで行われ、物理アドレスは最終的にCPUのレジスタに保存され、書き込み動作が発生する。

+0

私は参照してください。しかし、コミュニケーションはどうやって他の方法になりますか?私は、カーネル空間プログラムがユーザー空間に戻って通信できることを知っています。レジスタは保護されていませんか?何も利用できない場合はどうなりますか? –

+1

汎用レジスタは保護されておらず、使用するプログラム用です。実際、OSがタスクを切り替えるたびに、古いタスクのレジスタが保存され、新しいタスクのレジスタが復元されます。さらに、プログラムにレジスタを使用させないようにしても、あるメモリ位置から別のメモリ位置に値を移動することはできません( 'mov [loc2]、[loc1]'は無効です)。ほぼすべての操作のためにメモリにアクセスする)。 – glauxosdever

+0

この回答は、使用されている実際のシステムコールメカニズムを見落としており、レジスタやMMUの動作を使用してデータの転送を説明することはあまり正確ではありません。 – Flexo

2

既に知っているように、Windowsカーネルによってユーザー空間プログラムに公開されている多数の機能があります。 (あなたが好奇心なら、there's a list of system calls)。これらのシステムコールはすべて一意の番号で識別されますが、これはMicrosoftが公開している公開されているインターフェイスの一部ではありません。代わりに、あなたのプログラムから公開されている関数を呼び出すときには、通常の特権のないユーザーモード関数呼び出しであるエントリポイントを持つWindowsをインストール(または更新)するとDLLがインストールされます。このDLLは、現在実行中のカーネルでパブリックインターフェイスと使用可能なシステムコールの間のマッピングを認識します。これらのマッピングは常に1:1であるとは限らず、安定したインターフェイスを使用して既存のコードを破ることなく調整や拡張が可能です。

ユーザランドコードによっては、これらの関数のうちの1つを呼び出すときに、その役割はシステムコールの引数を準備してカーネルモードにジャンプすることです。ジャンプがどのくらい正確に発生するかは、Windowsが現在実行しているアーキテクチャに固有です。実際、それはx86とArmの間だけでなく、AMDとIntel x86システムの間でも変わります。ここでは、簡潔にするために、最新のIntel x86 32ビット・ケース(SYSENTER instructionを使用)について説明します。 x86では、他のバリエーションのほとんどは比較的小さなものです。たとえばint 2Eh was used prior to SYSENTER supportです。

起動時にオペレーティングシステムは、ユーザーランドとシステムコールを有効にするための準備作業を行います。これを理解することは、システムコールの実際の動作を理解するうえで重要です。

まず、少し巻き戻して、ユーザーランドとカーネルモードが何を意味するのかを考えてみましょう。 x86では、特権コードと特権コードについて、「リング」について話します。実際には4つあります(ハイパーバイザーは無視します)が、ring0(カーネル)とring3(ユーザーランド)以外は何も使用しませんでした。 x86上でコードを実行するとき、実行されているアドレス(EIP)と読み書き中のデータはセグメントから来ます。

セグメントは、ほとんどの場合、x86上の仮想アドレス指定が問題であった頃から残された歴史的な事故です。ただし、命令やその他の参照メモリを実行するときに現在どのセグメントが使用されているかを定義する特別なレジスタがあるため、これらは重要です。 x86上のセグメントは、すべてグローバルディスクリプタテーブルまたはGDTと呼ばれる大きなテーブルに定義されています。(ローカルデスクリプタテーブル、LDTもありますが、ここではこれ以上説明しません)。ここで議論する重要なポイントは、テーブルエントリの(秘密の)レイアウトには、現在アクティブなセグメントの特権レベルを定義するDPLという2ビットが含まれていることです。 2ビットが正確に4レベルの特権を定義するのに十分であることに気付くでしょう。

「カーネルモードで実行する」と言いますと、実際にはアクティブなコードセグメント(CS)とデータセグメントセレクタがDPLを0に設定したGDTのエントリを指しているだけです。 DPLが3に設定され、カーネルアドレスへのアクセスがない状態で、GDTエントリを指し示すCSセグメントおよびデータセグメントセレクタを有する。 (他のセレクタもありますが、シンプルにするために、今は "コード"と "データ"を考慮します)。

カーネルの起動時に最初に戻る:起動時にカーネルはGDT entries we needを作成します。 (これらは、SYSENTERが動作するためには特定の順序で配置する必要がありますが、ほとんどは実装の詳細です)。また、プロセッサの動作を制御する「マシン固有のレジスタ」もあります。これらは特権コードによってのみ設定できます。ここで重要なそれらのうち3つは、次のとおりです。

  • IA32_SYSENTER_ESP我々はリング0に移行したいユーザランド(RING3)にrunnigいくつかのコードを持っている
  • IA32_SYSENTER_EIP
  • IA32_SYSENTER_CS

リコール。呼び出し規約で必要とするレジスタをすべて保存し、呼び出しに必要な正しいレジスタに引数を格納すると仮定しましょう。その後、SYSENTER命令を実行します。 (実際はKiFastSystemCallと思う)。 SYSENTER命令は特殊です。これは、マシン固有のレジスタIA32_SYSENTER_CSのカーネル設定値に基づいて、現在のコードおよびデータセグメントセレクタを変更します。 (スタック/データセグメント値はIA32_SYSENTER_CSのオフセットとして計算されます)。その後、スタックポインタ自体(ESP)は、以前にシステムコールを処理するために設定されたカーネルスタックに設定され、MSR IA32_SYSENTER_ESPに保存され、IA32_SYSENTER_EIPからの命令ポインタも同様にEIPに格納されます。

DPLが0に設定された状態でCSセレクタがGDTエントリを指すようになり、EIPはこの時点でカーネルで実行しているカーネルスタック上のカーネルモードコードを指しています。

ここから、カーネルモードコードは、システムコールの実行に必要な実際の作業を行うために、カーネルとユーザースペースの両方からメモリを読み書きできます(適切な注意が必要です)。システムコールへの引数は呼び出し規約に従ってレジスタなどから読み取ることができますが、実際にはユーザランドに戻るポインタやカーネルオブジェクトのハンドルにアクセスして、大きなデータブロックを読み取ることもできます。

システムコールが終了すると、プロセスは基本的に逆になり、セレクタのDPL 3でuserlandに戻ります。

関連する問題