【Linux】プロセスアドレス空間

【Linux】プロセスアドレス空間

プロセスアドレス空間の概念

コンピュータ ソフトウェアおよびハードウェア リソースの管理者として、オペレーティング システムは当然のことながら、各プロセスのメモリ割り当てを管理する必要があります。したがって、オペレーティング システムは、各プロセスのメモリ割り当てを記述するデータ構造を持たなければなりません。このカーネル データ構造は、プロセス アドレス空間です。Linux オペレーティング システムでは、このデータ構造の変数名は ですmm_struct

プロセスアドレス空間の実装

メモリ割り当てをより適切に管理するために、mm_struct の実装では次の戦略が採用されています。

  • メモリ割り当てを記述するデータ構造として、メモリ空間全体を記述します。
  • メモリ内のアドレスを記述するには、連続した線形アドレスを使用します。
  • 記載されているアドレスはすべて仮想アドレスです。

mm_struct の疑似コードは次のとおりです。

struct mm_struct
{
    
    
    long code_begin; //代码区起始地址
    long code_end;	 //代码区结束地址
    //...
    long brk_begin;  //堆区起始地址
    long brk_end;	 //堆区结束地址
    long brk_begin;  //栈区起始地址
    long brk_end;	 //栈区结束地址
}

注:プロセスアドレス空間は連続した線形の仮想アドレスで記述されるメモリアドレスであるため、メモリ内の各領域は開始アドレスと終了アドレスによって区別できます。以下に示すように:

画像-20230826134746803

プロセスのアドレス空間は仮想アドレスを記述しますが、プロセスは依然としてメモリ内の実際のアドレスで独自のデータとコードを見つける必要があるため、オペレーティング システムはマッピングを使用して仮想アドレスを実際のメモリ アドレスに変換します。ページ テーブルはマッピング中に使用され、次のように、仮想アドレスと実際のメモリ アドレス間のマッピングを記録します。

画像-20230826135906135

コピーオンライトについて理解する

まず、コピーオンライト現象を観察するために次のコードを作成します。

#include <stdio.h>
#include <unistd.h>
#include <assert.h>

int main()
{
    
    
  int val = 100;
  pid_t id = fork();
  assert(id >= 0);
  if (id == 0)
  {
    
    
    //子进程
    while(1)
    {
    
    
      printf("我是子进程,pid:%d, ppid:%d, val:%d, &val:%p\n", getpid(), getppid(), val, &val);
      sleep(1);
      val = 200;
    }
  }
  else if (id > 0)
  {
    
    
    while(1)
    {
    
    
      printf("我是父进程,pid:%d, ppid:%d, val:%d, &val:%p\n", getpid(), getppid(), val, &val);
      sleep(1);
    }
  }
  return 0;
}

上記のコードをコンパイルして実行して、現象を確認します。

画像-20230826141508478

  • 子プロセスが val 変数の値を変更した後、子プロセスの val 値の変更は親プロセスの val 値に影響を与えません。
  • 親プロセスと子プロセスのvalアドレスは同じですが、値が異なります。

上記の親子プロセスはコード内の同じ変数を共有しており、どちらかが書き込みを試みると、両方がそれぞれコピーを使用します。これはコピーオンライトと呼ばれます。

まず、プロセスのアドレス空間内のアドレスはすべて仮想アドレスであるため、同じアドレスにある親プロセスと子プロセスによって読み取られるデータは異なるように見えます。子プロセスが作成されると、使用されるプロセスのアドレス空間とページ テーブルが次のように親プロセスからコピーされます。

画像-20230826142401503

子プロセスが val を変更すると、オペレーティング システムは、変更された値を記録するために子プロセス用のスペースを再度開き、次のように、ページ テーブルにマップされている実際のアドレスを変更してコピー オン ライトを実装します。

画像-20230826142642452

なぜプロセスアドレス空間があるのでしょうか?

プロセス アドレス空間が存在する理由を理解するには、まず次の 2 つのことを理解する必要があります。

マロックの本質

メモリ リソースには限りがあるため、メモリ空間の無駄を減らすために、オペレーティング システムは malloc でメモリを申請するときにページ テーブルの仮想アドレスにアドレスのみを埋め、ページ テーブル内の実際のメモリ アドレスは空になっています。プロセスがそれを使用するのを待っているときに、スペースを割り当て、実際のアドレスをページ テーブルに書き込みます。これにより、プロセスがスペースを申請してもそのスペースを使用しない場合に発生するスペースの無駄が回避されます。

実行可能プログラムのアドレス空間

ソース コードがコンパイルされるとき、コードとデータは、仮想アドレス空間にプログラムされた対応するアドレス指定方法に従ってコンパイルされます。

Linux システムでは、objdumop -S 可执行程序名プログラムの仮想アドレスを使用して以下を表示できます。

画像-20230826155041035

プロセスの実行開始後、CPU はまず仮想エントリ アドレスを実行し、そのアドレスも含まれており、エントリ アドレスに記録されている内容に従ってページ テーブルのジャンプを開始し、プロセスのコードとデータを通常どおり実行します。 。

なぜプロセスアドレス空間があるのでしょうか?

まず、プロセスのアドレス空間がない場合にオペレーティング システムがどのように動作するかを知る必要があります。プロセスのアドレス空間が無い場合にはページテーブルも存在せず、プロセスがメモリ上にロードされた後、CPUはプロセス内のコード順に実行します。次のように:

画像-20230826150931606

  • まず、この場合、プロセスのコードを実行するときにコードに問題があり、ワイルド ポインタのメモリ アクセス操作が発生すると、CPU も不正なアクセス操作を実行します。プロセスアドレス空間にはページテーブルもあります. データにアクセスするときは, ページテーブルを通じてマッピングする必要があります. ページテーブルはアドレスのマッピング関係を保存するだけでなく, 特定のアドレスの読み取りアドレスも保存します書き込み権限など、ワイルド ポインタが使用されている場合、ページ テーブルがそれをインターセプトします。
  • 第 2 に、この場合、プロセスがグローバル領域、静的領域、およびその他のパーティションに従ってデータを分割したい場合、物理メモリ内で実際のブロック アドレス全体を見つけて分割する必要があります。記憶の管理 共同作業が必要です。しかし、プロセス アドレス空間とページ テーブルを使用すると、プロセスはプロセス アドレス空間内のアドレスを領域に応じて分割できるかどうかだけを知る必要があり、物理メモリはメモリ内のどこにあるかに関係なく、その空間を見つけるだけで済みます。なぜなら、最終的にはすべてのデータ アクセスがページ テーブルを通じてマップされ、同じプロセス管理とメモリ管理が分離されるからです。
  • 第三に、この場合、コードとデータを別々に区別する必要があります。プロセス アドレス空間とページ テーブルを使用すると、プロセスはプロセス アドレス空間を介してコードとデータにアクセスするだけで済みます。すべてのプロセス アドレス空間で、コードとデータのパーティション アドレスは同じであるため、すべてのプロセスがビューを確認できます。自分のプロセスのアドレス空間に関しても同様です。

要約:

  1. アドレスへのランダム アクセスを防止し、物理メモリやその他のプロセスを保護します。
  2. プロセス管理とメモリ管理を分離します。
  3. すべてのプロセスにコードとデータの統一されたビューを提供します。

おすすめ

転載: blog.csdn.net/csdn_myhome/article/details/132522396