prog1.out としてビルドされた prog1.c があるとします。prog1.out には、elf がロードされる場所を示すリンカー情報があります。これらのアドレスは仮想アドレスになります。ローダーはこれらの情報を探し、これをプロセスとして起動します。DS、BSS などの各セクションは、リンカーで説明されているように、仮想アドレスに読み込まれます。たとえば、同じローダー アドレス、BSS、DS などを持つ prog2.out がありますが、競合しますか? 競合しないことはわかっていますが、パフォーマンスの問題が発生します。2 つのプロセスは同じ仮想アドレスを持っていますが、異なる物理アドレスにマップされているのでしょうか? 同じ仮想アドレスを持つ2つのプロセスをどのように保護できるか、私は混乱しています。
3 に答える
問題は、プロセスがメモリ アドレスを使用する場合、同じ物理アドレスとは異なる可能性がある仮想アドレスについて話していることです。これは、2 つのプロセスが同じアドレスを参照でき、それらのデータが混在しないことを意味します。これは、2 つの異なる物理的な場所にあるためです。
以下に、一般的なコンピューターで仮想アドレスが物理アドレスに変換される方法について説明します (これは他のアーキテクチャーでは少し異なりますが、考え方は同じです)。
Intel x86 アーキテクチャ (3 レベルのページング) でのメモリ変換プロセスを理解する
したがって、一方で仮想メモリ アドレスがあり、物理メモリ アドレス (つまり、RAM 上の実際のアドレス) を取得したい場合、ワークフローは主に次のようになります。
仮想アドレス -> [分割単位] -> [ページング単位] -> 物理アドレス
各オペレーティング システムは、セグメンテーション ユニットとページングの動作方法を定義する場合があります。たとえば、Linux はフラット セグメンテーション モデルを使用します。これは、それが無視されることを意味するため、ここで同じことを行います。
さて、私たちの仮想アドレスは、ページング ユニットと呼ばれるものを通過し、何らかの形で物理アドレスに変換されます.. これがその方法です。
メモリは特定のサイズのブロックに分割されます。インテルでは、このサイズは 4KB または 4MB です。
各プロセスは、メモリ内の一連のテーブルを定義するため、コンピュータはメモリ アドレスをどのように変換する必要があるかを認識します。これらのテーブルは階層的に編成されており、実際には、アクセスするメモリ アドレスはこれらのテーブルのインデックスに分解されます。
わかりにくいかもしれませんが、あと数文お待ちください。この画像で私の文章をたどることができます:
最初のテーブルのベース アドレスを格納するCR3と呼ばれる内部 CPU レジスタがあります (このテーブルをページ ディレクトリと呼び、そのエントリのそれぞれをページ ディレクトリ エントリと呼びます)。プロセスが実行されると、そのCR3がロードされます (とりわけ)。
したがって、たとえば、メモリアドレス0x00C30404にアクセスしたいとします。
ページング ユニットは、「OK、ページ ディレクトリ ベースを取得しましょう」と言い、CR3 レジスタを見て、ページ ディレクトリのベースがどこにあるかを認識します。このアドレスを PDB (ページ ディレクトリ ベース) と呼びましょう。
ここで、どのディレクトリ エントリを使用する必要があるかを知りたいと思います。前に述べたように、アドレスは一連のインデックスに分解されます。最上位 10 ビット (ビット 22 から 31) は、ページ ディレクトリのインデックスに 対応 します。0x3である11。これは、3 ページ目のディレクトリ エントリを探したいことを意味します。
今何をすべきか?
これらのテーブルは階層的であることに注意してください。各ページ ディレクトリ エントリには、とりわけ、Page Tableと呼ばれる次のテーブルのアドレスがあります。(このテーブルは、ページ ディレクトリ エントリごとに異なる場合があります)。
これで、別のテーブルを取得できました。アドレスの次の 10 ビットは、このテーブルのどのインデックスにアクセスするかを示します (ページ テーブル エントリと呼びましょう)。
00 0011 0000 は次の 10 ビットであり、それらは数値: 0x30です。これは、30 番目のページ テーブル エントリにアクセスする必要があることを意味します。
最後に、このページ テーブル エントリは、目的のページ フレームのオフセットを保持します(メモリは 4k のブロックに分割されていることに注意してください)。最後に、アドレスの最下位 12 ビットは、このPAGE FRAMEのメモリ オフセットです。PAGE FRAMEは実際の物理メモリ アドレスであることに注意してください。
これは 3 レベルのページングと呼ばれ、64 ビット (または PAE を使用) では非常に似ていますが、もう 1 つのレベルのページングがあります。
変数をフェッチするためだけにこれらすべてのメモリアクセスを取得するのは本当に残念だと思うかもしれません..そしてそれは本当です. コンピューターには、これらすべての手順を回避するためのメカニズムがあり、そのうちの 1 つが TLB (テーブル ルックアサイド バッファー) であり、実行されたすべての変換のキャッシュを格納するため、メモリを簡単に取得できます。
また、これらの構造の各エントリには、「このページは書き込み可能ですか?」などのアクセス許可に関するいくつかのプロパティがあります。「このページは実行可能ですか?」.
メモリー・ページングがどのように機能するかを理解したので、Linux がメモリーをどのように処理するかを理解するのは簡単です。
- 各プロセスには独自の CR3 があり、プロセスの実行がスケジュールされると、cr3 レジスタがロードされます。
- 各プロセスには独自の仮想空間があります (テーブルがいっぱいではない場合があります。プロセスは、たとえば 4kb の 1 ページだけで開始できます)。
- 各プロセスは他のいくつかのページ (オペレーティング システム メモリ) をマップしているため、割り込みを受けると、割り込みハンドラーが同じタスクで開始され、そのタスク内で割り込みを処理して、必要なコードを実行します。
この種のスキームのいくつかの動機
- すべてのプロセスのメモリを同時に持つ必要はありません。一部をディスクに保存できます。
- 各プロセス メモリを安全に分離し、いくつかのアクセス許可を与えることもできます。
- プロセスは 10MB の RAM を使用する場合がありますが、物理メモリ内で連続している必要はありません。
仮想アドレスと物理アドレスの間には関係がありません (一般的なマシンでは)。マッピングは常に発生します。これは、そもそも仮想アドレスの目的の一部です。
「パフォーマンスの問題」は、あったとしても、常に存在し、ハードウェアの内部に隠されています。実際の衝突があったかどうか (これらのアドレスが物理的なものだった場合)。
同じ仮想アドレスを持つ 2 つのプロセスをどのように保護できるか。
あなたはしませんし、する必要もありません。各プロセスのメモリの内容は、他のプロセスとは独立しており、それを保証するのは OS とハードウェアの仕事です。「仮想メモリ」という用語は、「プロセス固有の独自のメモリ」と考えることができます。
この説明はすべて、オブジェクト コードの内容やリンク/ロード プロセスとはほとんど関係がないことに注意してください。実行時に任意のアドレスを参照でき、リンカー/ローダーはそれを知る必要はありません。