【Linux】プロセスの概念(その2)

1. 環境変数

1. コマンドラインパラメータ

コマンドラインパラメータとは何ですか?まず、main 関数がパラメータを渡すことができることを知っておく必要があります。 main 関数に渡されるパラメータはコマンド ライン パラメータです。

次のように、main 関数を作成し、main 関数でコマンド ライン パラメータを受け取ることができます。コマンド ライン パラメータを出力して観察します。

ここに画像の説明を挿入します

画像内の argcargv は受信するものですコマンド 行パラメータの仮パラメータについては、出力されたデータを観察してみましょう。

ここに画像の説明を挿入します

出力されたデータは実際には実行可能プログラムの名前であることがわかります。では、0 は何を表しているのでしょうか?次のように、実行可能プログラムの後にデータを追加してみましょう。

ここに画像の説明を挿入します

最終的に結論に達しました。区切り文字としてスペースを使用して入力したコマンドは、4 の部分文字列と This に分割されました。 a>4 部分文字列は最終的に main 関数に渡されて受信されます。argc はコマンド ライン パラメータの数、argv は分割された部分文字列です。そのうち 0 部分文字列番号は実行可能プログラムである必要があります。以下はオプションであると言えます。なぜそれがオプションと呼ばれるのですか? 異なる部分文字列を通じて異なるコードを実行できるためです。たとえば:

		  1 #include <stdio.h>
		  2 #include <string.h>
		  3 
		  4 int main(int argc, char* argv[])
		  5 {
		  6     if(argc != 2)
		  7     {
		  8         printf("error!\n");
		  9         return 1;                                                                                                                 
		 10     }
		 11     if(strcmp(argv[1], "-a") == 0)
		 12         printf("aaa\n");
		 13     else if(strcmp(argv[1], "-b") == 0)
		 14         printf("bbb\n");
		 15     // ........
		 16 
		 17     return 0;
		 18 }

上記のコードに示されているように、コマンド ライン パラメーターを通じて、さまざまなコマンド レベルでのコマンド ライン オプションの設定をサポートできます。

2. 共通の環境変数

  1. 環境変数は通常、オペレーティング システムの動作環境を指定するためにオペレーティング システムで使用されるいくつかのパラメータを指します。
  2. たとえば、C/C++ コードを作成する場合、リンク時にリンクされた動的ライブラリと静的ライブラリがどこにあるかは決してわかりませんが、正常にリンクして実行可能プログラムを生成することはできます。その理由は、コンパイラを支援する関連する環境変数があるためです。検索;
  3. 環境変数には通常、いくつかの特別な目的があり、システム内でグローバルな特性を持っています。

一般的な環境変数には次のものがあります:

  • PATH: コマンドの検索パスを指定します。
  • HOME: ユーザーのホーム作業ディレクトリ (つまり、ユーザーが Linux システムにログインするときのデフォルトのディレクトリ) を指定します。
  • SHELL: 現在のシェル。その値は通常 /bin/bash です。

(1)パス

PATH とは何ですか?通常、Linux でコードを作成します。コードを実行したい場合は、まず実行可能プログラムのパスを見つける必要があります。たとえば、以下に示すように、パスの下に ./ を前に追加する必要があります。

ここに画像の説明を挿入します

上記のコマンド ライン パラメータの調査により、Linux< a i=4> 内の命令も実行可能プログラムですが、./ を追加しなくても命令が正常に実行できるのはなぜですか?これは、環境変数 PATHPATH に関連しています。 を追加する必要はありません。 ./ に追加するだけです。を実行するために PATH。プログラムのパスをシステムのデフォルトの検索パス

コマンド which + 指令 を使用すると、このコマンドが配置されているパスを表示できます。たとえば、ll コマンドが配置されているパスを表示する必要があります。場所:

ここに画像の説明を挿入します

次に、PATH の内容を確認し、コマンド echo $PATH を使用して表示します。 > の構文です。次のように: shell はビューの参照解除の意味に相当します。これは $

ここに画像の説明を挿入します

現在のパスを PATH に追加したい場合はどうすればよいでしょうか?まず pwd を使用して現在のパスを表示します。

ここに画像の説明を挿入します

次に、パスをコピーして次の場所に置きます。次のコマンドを使用できます。

		PATH=$PATH:/home/lmy/.mygitee/Linux_Study/study9

この時点で、パスが PATH に追加されました。確認できます。

ここに画像の説明を挿入します

上に示したように、PATH にパスを追加しました。その後、現在のパスで実行可能プログラムを実行すると、以下に示すように、 ./ を前に追加する必要はありません。

ここに画像の説明を挿入します

現在のパスを削除したい場合はどうすればよいでしょうか?これも非常に簡単です。不要な部分を削除するだけです。PATH 内の現在のパスを削除すると、現在のパス以外の部分をコピーできるとします。 . パスを指定し、次のコマンドを実行します。

	PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/lmy/.local/bin:/home/lmy/bin

は現在のパスを削除します。PATH:< a i で始まることに注意してください。 =4> は、異なるパスを区切るための区切り文字として使用されます。

上記は 1 つの方法ですが、cp コマンドを直接使用して実行可能プログラムを PATH< にコピーするという別の方法もあります。 /span>は、パスから実行可能プログラムを削除することを意味します。 プログラムのアンインストールと呼ばれます。本質は、システムが見つけられるパスに実行可能プログラムをコピーすることです。プログラムのインストールsudo を使用してください。ここでは説明しません。この方法は のパス内のファイルの下に実装することもできます。ここでコピーするには、昇格されたアクセス許可が必要であることに注意してください。

PATH を空の文字列に設定すると、以下のように何が起こるかを見てみましょう:

ここに画像の説明を挿入します

ほとんどのコマンドが使用できなくなっていることがわかります。この時点で、Xshell を再起動して回復できます。したがって、デフォルトでの環境変数の変更は今回のログインに限定されており、再度ログインすると環境変数は自動的に復元されると結論付けられます。それについては後で話します。

(2)障害者

pwd コマンドを使用するとき、システムはパスの場所をどのようにして認識するのでしょうか?その理由は、Linux にも環境変数があるためです: PWD .

PWD は、現在対応するbash 組み込みのもので、私たちが何であるかを記録するために特別に使用されます。のパスの環境変数。

たとえば、まず現在のパスを確認してみましょう。

ここに画像の説明を挿入します
しましょうcd 上のレベルに移動して、もう一度確認してください。PWD 環境変数:

ここに画像の説明を挿入します

上に示したように、PWD 環境変数もそれに応じて変更されるため、pwd < a i=4> のコマンドは、実際には PWD 環境変数の内容を読み取り、出力します。

(3)ホーム

デフォルトでログインする場合Xshell、現在のパス/ディレクトリは/home/xxxです。例:

ここに画像の説明を挿入します

しかし、root ユーザーとしてログインすると、デフォルトのディレクトリは/rootになります。 ;どうしてこれなの?

これは、ログインするときに、まずユーザー名とパスワードを入力し、システム認証を待つ必要があるためです。認証が完了すると、環境変数が形成され、複数の環境変数が存在する必要があります。この時点では (PATH、PWD、HOME など) ; その後、HOME がユーザー名、つまり に従って初期化されます。 HOME = /root, HOME = /home/xxx a>cd を実行するだけです。 $HOME; 最後に、

(4) env すべての環境変数を表示

すべての環境変数を表示するには、env コマンドを使用できます。Linux には多くの環境変数があります。自分で確認できます。各環境変数には独自の特別な目的があり、特定のシステム機能を完了するために使用されます。

3. 環境変数を取得する

(1) コードを通じて環境変数を取得する

コードを通じて環境変数を直接取得できるインターフェイス (getenv) があります。man

ここに画像の説明を挿入します

次のコードに示すように、getenv を使用して PATH 環境変数を確認できます。 /span>

		  1 #include <stdio.h>
		  2 #include <stdlib.h>
		  3 
		  4 int main()
		  5 {
		  6     printf("%s\n", getenv("PATH"));
		  7     return 0;                                                                                                                     
		  8 }   

実行結果は以下の通りです。

ここに画像の説明を挿入します

上で説明したすべての環境変数には、それぞれ独自の特別な目的があります。それを反映するにはどうすればよいでしょうか?次のように、 getenv と組み合わせて単純に使用してみましょう。

		#include <stdio.h>
		#include <stdlib.h>
		#include <string.h>
		
		int main()
		{
		    char* s = getenv("USER");
		    if(strcmp(s, "root") != 0)
		    {
		        printf("%s 是非法用户!\n", s);
		        return 1;
		    }
		    printf("Hello!\n");
		    printf("Hello!\n");
		    printf("Hello!\n");
		    return 0;
		}

環境変数USERを使用して、現在のユーザーがroot<であるかどうかを判断します。 a i =4> の場合、root に対応するコードのみを実行させます。結果は次のとおりです。

当我们是 root 用户:

ここに画像の説明を挿入します

私たちが一般ユーザーの場合:

ここに画像の説明を挿入します

(2) コマンドラインパラメータから取得

1. 上記で学んだコマンド パラメータを通じて、2 つのコマンド ライン パラメータが存在する可能性があることがわかりましたが、実際には 3 番目のコマンド ライン パラメータも存在する可能性があります。つまり環境!

env は実際には環境変数テーブルです。システムがプログラムを開始するときに、プロセス (メイン) に 2 つのテーブルを提供することを選択できます: 1. コマンド ライン パラメータ テーブル 2 . 環境変数テーブル。3 番目のパラメータ env は環境変数テーブル、env は、関数ポインタの配列を指すポインタです。理解するには、次の図を参照してください。

ここに画像の説明を挿入します

右側の大きなリストは環境変数です。通常、env の最後の要素は空を指します。

コードを出力して観察することもできます。次のコードに示すように、現在実行中のプログラムの PID を出力することもできます。 :

		    1 #include <stdio.h>
		    2 #include <stdlib.h>
		    3 #include <string.h>
		    4 #include <unistd.h>
		    5 #include <sys/types.h>
		    6 
		    7 int main(int argc, char* argv[], char* env[])
		    8 {
		    9     int i = 0;
		   10     for(; env[i]; i++)
		   11     {
		   12         printf("pid: %d, env[%d]: %s\n", getpid(), i, env[i]);                                                                  
		   13     }
		   14 
		   15     return 0;
		   16 }

の実行結果は自分で表示できますが、なぜその pid を出力するのでしょうか?そこで、このプロセスの env を誰が私たちに渡したのか、考えてみたほうがよいでしょう。それはバッシュです。コマンド ラインによって開始されたプロセスはすべてshell(bash) の子プロセスです。子プロセスのコマンド ライン パラメータと環境変数はすべて親プロセスです。プロセス a> が成功しました。 bash

そこで再び疑問が生じます。親プロセスbashの環境変数はどこから来るのでしょうか?実際、環境変数はスクリプト設定ファイルの形式で存在します。このファイルも見つけることができます。たとえば、ホーム ディレクトリに戻ると、次のように .bash_profile という隠しファイルがあります。以下:

ここに画像の説明を挿入します

ログインするたびに、bash プロセスはこのファイルの内容を読み取り.bash_profilebash プロセスの新しい環境変数テーブル情報!これは、上記の問題の 1 つを説明することもあります。 PATH のパスを空に変更し、 Xshell を再起動しました。 a> その後は通常に戻ります。

2. もう 1 つの質問ですが、以下に示すように、コマンド ラインに直接入力するだけで、独自の環境変数を作成することもできます。

ここに画像の説明を挿入します

この時点で、環境変数テーブルをチェックインします。

ここに画像の説明を挿入します

これは環境変数テーブルにインポートされていないことがわかりました。実行可能プログラムで直接見つけることができるかもしれません。

ここに画像の説明を挿入します

も、環境変数テーブルにインポートしていないため、使用できません。次のように、コマンド export MYENV_LMY を使用して、環境変数テーブルにインポートする必要があります。 /a >

ここに画像の説明を挿入します

環境変数を作成するときに、次のコマンドを使用して直接インポートすることもできます。

export 环境变量名称=内容、以下に示すように:

ここに画像の説明を挿入します

両方とも環境変数テーブルに表示されていることがわかります。ログアウトして再度ログインした場合でも、それらはまだ存在しますか?答えはもうありません!なぜなら、上記の 2 つの環境変数は設定ファイルに書き込まれていないからです。変更されたのは、現在のbash内部環境変数テーブルのみです。ログアウトして再度ログインすると、bash .bash_profile ファイルを再読み込みして、環境変数テーブルを再取得します。上記はbash プロセス内でのみ有効であり、ローカル変数と呼ばれます。 !

独自の環境変数を永久に有効にするには、以下に示すように.bash_profile設定ファイルに追加する必要があります。

ここに画像の説明を挿入します

この時点で保存して終了し、再度ログインすると、対応する環境変数を見つけることができます。

ここに画像の説明を挿入します

子プロセス継承による環境変数テーブルの継承は環境変数と呼ばれます。

コマンド ライン パラメータ テーブルと環境変数テーブルの両方が子プロセスに継承できることがわかっています。これは、システム環境変数がグローバル属性を持っていることを示しています。 a>!

(3) extern char** 環境から取得

環境変数を取得する 2 番目の方法では、コマンド ライン パラメータを記述して渡す必要があります。コマンド ライン パラメータを記述せずに環境変数テーブルを取得する方法もあります。これはシステムによって提供されます。< a i=1>environ ポインタ。このポインタを使用するときは、extern を使用して宣言する必要があります。 、それは私たち自身のものではないためです。定義はシステムによって提供されます。実際、environ を指します。 env ポインタ、それらの関係は次のとおりです。

ここに画像の説明を挿入します

次のコードに示すように、environ を通じて環境変数テーブルを出力することもできます。

		  1 #include <stdio.h>
		  2 #include <stdlib.h>
		  3 #include <string.h>
		  4 #include <unistd.h>
		  5 #include <sys/types.h>
		  6 
		  7 int main()
		  8 {
		  9     extern char** environ;
		 10     int i = 0;
		 11     for(; environ[i]; i++)
		 12     {
		 13         printf("pid: %d, environ[%d]: %s\n", getpid(), i, environ[i]);                                                            
		 14     }
		 15 
		 16     return 0;
		 17 }

実行結果を自分で試して観察することもできますが、結果は 2 番目の方法と同じです。

4. ローカル変数と環境変数

上記では、ローカル変数と環境変数についても簡単に紹介しましたが、次に、それらの違いをさらに分析します。

  • ローカル変数: ローカル変数はbash プロセス内でのみ有効であり、子プロセスには継承されません。
  • 環境変数: 環境変数は、すべての子プロセスに継承させることで、そのグローバルな性質を実現します。環境変数はすべての子プロセスに当然継承されます。

5. Linuxコマンドの分類

最初に、PATH を空に設定すると、実行できるコマンドと実行できないコマンドがあることを思い出します。以下に示すように、もう一度試してみましょう。

ここに画像の説明を挿入します

一部のコマンドは実行できないことがわかりますが、pwd や echo などのコマンドはなぜ実行できるのでしょうか。その理由は、Linux のコマンドが 2 つのカテゴリに分類できるためです。

  1. 継続命令

通常のコマンドはシェルコマンドラインインタープリタフォーク子プロセスを実行させます。

  1. 組み込みコマンド

組み込みコマンドはシェルコマンドラインの機能であり、シェル上に構築されます。 /span> 内で定義されたローカル変数を直接読み取ることができます。 シェル なので、

6. 環境変数に関するコマンド

  • エコー

echo は環境変数の値を表示することです。これは上でも使用しました。たとえば、PATH< を表示する必要があります。 /span>環境変数値:

ここに画像の説明を挿入します

  • 輸出

export は、ローカル変数を環境変数テーブルにインポートします。つまり、上記でも使用した新しい環境変数を設定します。たとえば、最初にローカル変数を設定します。この時点では、 のため、echo を使用してそれを表示できます。 echo は組み込みコマンドですが、以下に示すように、 環境変数env にはそのようなローカル変数はありません。 :

ここに画像の説明を挿入します

このローカル変数を環境変数にインポートし、env で確認できます。

ここに画像の説明を挿入します

  • 設定を解除する

unset は環境変数をクリアし、環境変数を削除します。たとえば、次のように、上で定義した環境変数 MYENV を削除したいとします。

ここに画像の説明を挿入します

現時点では、ローカル変数と環境変数はありません。

  • 環境

env には、上でも紹介したすべての環境変数が表示されます。

  • セット

set は、ローカルに定義されたシェル変数と環境変数を表示します。たとえば、 ローカル変数local_env と環境変数myenvを定義します。 、これ 環境変数を確認すると、以下に示すように、myenv のみが表示されます。

ここに画像の説明を挿入します

ただし、以下に示すように、set を使用して表示すると両方を確認できます。

ここに画像の説明を挿入します

2. プログラムのアドレス空間

1. プログラムのアドレス空間の配分

次の図のようなC/C++の空間分布図を大まかに理解しました。

ここに画像の説明を挿入します

そのうちヒープとスタックは相対的なものです。ヒープ領域は上に向かって増加し、スタック領域は下に向かって増加します。実際、ヒープは領域とスタック 領域の中央には他のスペースもありますが、これについては後ほど学習します; データ セグメントには静的データ変数も存在します 実際、静的変数はコンパイラによってグローバル変数に変更されるため、データセグメントに配置されます。

プログラムのアドレスが上記の空間に従って分布していることを証明するにはどうすればよいでしょうか?以下に示すように、コードを使用して確認してみましょう。

		    1 #include <stdio.h>
		    2 #include <stdlib.h>
		    3 
		    4 int init_val = 1;
		    5 int uninit_val;
		    6 
		    7 int main()
		    8 {
		    9     char* str = "Hello";
		   10     char* heap1 = (char*)malloc(10);
		   11     char* heap2 = (char*)malloc(10);
		   12     char* heap3 = (char*)malloc(10);
		   13     char* heap4 = (char*)malloc(10);
		   14     static int static_val1 = 2;
		   15     static int static_val2;
		   16 
		   17     printf("栈区地址1:%p\n", &heap1);
		   18     printf("栈区地址2:%p\n", &heap2);                                                                                           
		   19     printf("栈区地址3:%p\n", &heap3);
		   20     printf("栈区地址4:%p\n", &heap4);
		   21 
		   22     printf("堆区地址4:%p\n", heap4);
		   23     printf("堆区地址3:%p\n", heap3);
		   24     printf("堆区地址2:%p\n", heap2);                                                                                            
		   25     printf("堆区地址1:%p\n", heap1);
		   26 
		   27     printf("未初始化静态变量地址:%p\n", &static_val2);
		   28     printf("未初始化全局数据区:%p\n", &uninit_val);
		   29     printf("已初始化静态变量地址:%p\n", &static_val1);
		   30     printf("已初始化全局数据区:%p\n", &init_val);
		   31     printf("字符常量区地址:%p\n", str);
		   32     printf("代码区地址:%p\n", main);
		   33 
		   34     return 0;
		   35 }

実行結果は次のとおりですが、実際に結果は次のようになります。

ここに画像の説明を挿入します

分析のためにスタック領域を個別に取得します。ローカルで作成する配列と構造は、スタック領域のスペースを空けるために下方向に成長します。a[ 10]< /span> より大きなアドレスを持っているのは誰ですか?次のように、それを検証するプログラムを作成してみましょう。 構造体、then および &obj.xA obj? a[9]a[0]struct A = {x, y, z} 配列、

		   #include <stdio.h>
		   #include <stdlib.h>
		   
		   typedef struct A                                                                                                                  
		   {
		       int x;
		       int y;
		       int z;
		   }A;
		  
		  int main()
		  {
		      int a[10];
		      A obj;
		      printf("%p\n", &a[0]);
		      printf("%p\n", &a[9]);
		      printf("%p\n", &obj.x);
		      printf("%p\n", &obj.y);
		      printf("%p\n", &obj.z);
		      
			  return 0;
		 }

結果は次のとおりです。

ここに画像の説明を挿入します

したがって、スタック領域はアプリケーション空間を下方向に開きますが、使用される場合は部分的に上方向に使用されるという結論に達しました。これは次の図のように要約できます。

ここに画像の説明を挿入します

上に示したように、スタック上で変数 int b を定義すると、int が 4 バイトを占め、上記の空間内の各スペースが 1 バイトを占めることがわかります。 どのアドレスを取得しましたか?実際には、最小アドレスが取得され、開始アドレス + オフセットを通じてアクセスされます。 &b

実は上記の空間分布の領域以外にもまだ学習していない領域があるので後ほど紹介しますが、スタック領域には2つの領域が存在します。これは< a i=1>コマンド ライン パラメータと環境変数です。

では、上で学んだ空間分布とは正確には何でしょうか?記憶ですか?以下から学習を始めましょう。

2. プロセスのアドレス空間

まず、前にフォークを学習したときに親プロセスと子プロセスがどのように実行されたかを確認してみましょう。まだ解決されていない問題が 1 つあります。子プロセスがコードを変更するとコピーオンライトが発生するということですが、変数の異なる値が同じアドレスを持つのはなぜでしょうか?次に学習する必要があるのはこれです。まず、次のようにコードを入力します。

		  1 #include <stdio.h>
		  2 #include <stdlib.h>
		  3 #include <unistd.h>
		  4                                                                                                                                   
		  5 int g_val = 100;
		  6 
		  7 int main()
		  8 {
		  9     pid_t id = fork();
		 10     if(id == 0)
		 11     {
		 12         //child
		 13         int cnt = 5;
		 14         while(1)
		 15         {
		 16             printf("i am child, g_val: %d, &g_val=%p\n", g_val, &g_val);
		 17             sleep(1);
		 18             if(cnt == 0)
		 19             {
		 20                 g_val=200;
		 21                 printf("child change g_val: 100->200\n");
		 22             }
		 23             cnt--;
		 24         }
		 25     }
		 26     else
		 27     {                                                                                                                             
		 28         //father
		 29         while(1)
		 30         {
		 31             printf("i am father, g_val: %d, &g_val=%p\n", g_val, &g_val);
		 32             sleep(1);
		 33         }
		 34     }
		 35     sleep(100);
		 36     return 0;
		 37 }

グローバル変数g_valを定義しました。子プロセスがそれを変更しても、親プロセスは同じままです< a i= 3>g_val は、親プロセスと子プロセスの間のデータは互いに影響を与えず、独立しているため、実行結果を観察すると、それらのアドレスが実際に同じであることがわかります。以下に示すように:

ここに画像の説明を挿入します

では、なぜ同じアドレスを読み取ると異なる内容が生じるのでしょうか?したがって、上の図から、C/C++ で通常見られるアドレスは明らかに物理アドレスではなく、実際には仮想アドレス/リニア アドレスであるという結論が導き出されます。

実際には、上で学んだスペース分布の図はプロセス アドレス スペースであり、その中のアドレスはすべて仮想アドレスです。以下に示すように:

ここに画像の説明を挿入します

ただし、プロセスは CPU によってスケジュールされる必要があります。プロセス内のデータが CPU によって読み取られて認識されるためには、データがメモリ、つまり物理メモリにロードされる必要があります。では、このプロセスは具体的にどのようなものなのでしょうか?実際、上記のコードによって出力されるアドレスはすべて、そのプロセス アドレス空間のアドレス、つまり仮想アドレスであり、この実行可能プログラムはbashです。の子プロセスです!そして、この親プロセスはコード内に独自の子プロセスを作成し、独自のプロセス アドレス空間も持っているため、すべてのプログラムが実行されると、プロセス アドレス空間が存在すると考えられます。

これで、pcb(task_struct)、プロセス アドレス空間、物理メモリという 3 つの重要な役割ができました。その中には <独自のプロセスのアドレス空間を指す /span>あ>0x11111111 のように物理アドレスと呼ばれます。したがって、現在の関係は次のとおりです。0x11111111 内の一部のフィールドであり、プロセスの一部のデータも物理メモリに保存する必要があります。このデータがメモリ内で保存されるアドレスは、task_struct

ここに画像の説明を挿入します

では、プロセスのアドレス空間と物理メモリはどのように接続されているのでしょうか?オペレーティング システムでは、プロセスが物理メモリ内で独自のデータを見つけるために、 マッピング テーブル がプロセスごとに維持されます。スペースと物理メモリの間にマッピング関係が構築され、このテーブルはページ テーブルと呼ばれます。左側が仮想アドレス、右側が物理メモリ上のデータのアドレスであり、両者の間にはマッピング関係があります。次の図で理解できます:

ここに画像の説明を挿入します

上記はページテーブルの簡単な構造ですが、ページテーブルには後ほど紹介するフィールドが他にもあり、その中でプロセスはページテーブルを検索し、物理メモリに対応する物理アドレスをアドレスから見つけることができます。プロセスのアドレス空間。

上記のことから、プロセスの実行後にプロセスのアドレス空間が存在し、システム レベルではすべてが独自のページ テーブル マッピング構造を持っていることがわかります。これは、上記の親プロセスにも子プロセスがあるためです。したがって、オペレーティング システムは親プロセスのpcbを子プロセスにコピーするため、子プロセスも独自のプロセス アドレス スペースとページ テーブルを持つことになります。 , これらはすべて親プロセスから取得されます。次の図のように、互いに独立しており、相互に影響しません。

ここに画像の説明を挿入します

子プロセスがデータを変更するとき、ページ テーブルを通じて対応するデータを見つけますが、オペレーティング システムは親プロセスがこのデータを使用していることを認識します。そのため、子プロセスは独立しているため、データを直接変更できません。物理メモリの値は書き込み時にコピーされて新しい物理アドレスが生成され、子プロセスが変更したいデータがその間のデータを上書きします。このとき、子プロセスの物理アドレスは次の図の理解と組み合わせて変更します。

ここに画像の説明を挿入します

したがって、2 つのプロセスが独立したプロセス アドレス空間とページ テーブルを持っているため、同じアドレスで異なる値を持つ理由がわかります。コピー オン ライトは物理メモリで発生し、物理アドレスも変更されます。仮想アドレスは変更されないため、同じ仮想アドレスは相互に影響を与えません。

3. アドレス空間とエリア分割

(1) アドレス空間

上記のことから、物理メモリはオペレーティング システムによって直接管理されていることがわかります。プロセスがスペースを使用する必要がある場合、オペレーティング システムはプロセスに仮想アドレス スペースを与えます。この仮想アドレス スペースがアドレス スペースです。プロセスが増えると、オペレーティング システムはプロセスを管理する必要があるため、アドレス空間も管理する必要があります。アドレス空間が管理されていないと、すべてが台無しになってしまいます。では、どうやって管理すればよいのでしょうか?それについては以下で説明します。

(2) 地域区分

いわゆる地域分割では、startendint または long long 変数開始と終了。スペースのサイズは調整できます。調整する場合は、開始終了だけを行う必要があります。 a> 値を拡張または縮小するだけで、スペースの範囲を確認するだけでなく、スペース範囲内のすべてのアドレスを使用できます。

(3)アドレス空間の管理

各プロセスにはアドレス空間があり、アドレス空間はシステム内で管理される必要があるため、アドレス空間はオペレーティング システムによって管理される必要があります。どのように管理すればよいでしょうか?以前に学習したように、最初に記述し、次に整理するので、アドレス空間は最終的にはカーネル データ構造オブジェクト、つまりカーネル構造でなければなりません。

プロセスのアドレス空間の内容はすべて領域に分割されていることがわかりました。エリアを分けて管理できるんですね!

Linux では、このプロセス/仮想アドレス空間これはstruct mm_structと呼ばれ、おそらく次のようになります:

		struct mm_struct
		{
			long code_start;
			long code_end;
			long data_start;
			long data_end;
			long heap_start;
			long heap_end;
			long stack_start;
			long stack_end;
			......
		}

ここでstruct mm_struct、この構造体は struct mm_struct* mm というポインタによってポイントされており、このポインタは task_struct に存在します。各プロセスが作成されると、 struct mm_struct* mm が作成され、プロセス アドレス空間が存在します。では、なぜプロセス アドレス空間がメモリではないのでしょうか? その理由は、それが単なるカーネル データ構造だからです。 。

4. なぜアドレス空間が必要なのでしょうか?

  1. プロセスに統一された観点からメモリを表示できるようにすることで、どのプロセスでも、アドレス空間とページ テーブルを通じて無秩序なメモリ データを整理し、カテゴリに分けて計画することができます。

なぜそんなことを言うのですか?考えてみましょう。プログラムがメモリにロードされるとき、順番にロードされますか?いいえ、プログラムは順不同でメモリにロードされますが、アドレス空間とページ テーブルを通じて、順不同のメモリ データを正しい順序に変更することができ、プロセスにデータがその方法に従って分類されていると認識させることができます。

  1. 仮想アドレス空間があり、メモリへのプロセス アクセスのセキュリティ チェックを効果的に実行できます。

まず、ページ テーブルには アクセス許可フィールド という別の列があることを理解する必要があります。その構造は次のとおりです。以下に続きます:

ここに画像の説明を挿入します

アクセス権フィールドは何に使用されますか?物理メモリにアクセスするときに、対応する権限が満たされているかどうか、またはアドレスが正当であるかどうかをチェックします。権限が満たされていない場合、またはアドレスが不正な場合は、ページ テーブルで直接インターセプトされ、物理メモリ内のアドレスにアクセスします。許可されません。

たとえば、次のコード:

		int main()
		{
			char* s = "hello";
			*s = 'h';
			return 0;
		}

上記のコードは正常に実行できません。"hello" は定数で変更できないことがわかっているのに、なぜこれが Woolen なのかというと、布?この時点で、s のアドレスがプロセス アドレス空間の文字定数領域にあり、文字定数によってマップされたページ テーブルにあることがわかります。エリア アクセス許可フィールドは読み取り専用、つまり r であるため、変更を書き込む必要がある場合、ページ テーブルで直接インターセプトされます。

さらに、このプロセスはさまざまな変換とさまざまなアクセスを実行するため、このプロセスが実行されている必要があります。したがって、cpu には CR3 というレジスタがあり、このレジスタにページが保存されます。 table このページテーブルのアドレスは物理アドレスです。このプロセスはcpuで実行されているため、CR3の内容はプロセスのハードウェアコンテキストで!したがって、プロセスが切り替わると、基本的に CR3 レジスタの内容が現在のプロセスのコンテキストに保存されます。したがって、各プロセスには独自のページテーブルがあります。

  1. プロセス管理とメモリ管理を分離してください。

オペレーティング システム レベルでは、メモリにロードする必要があるプログラムがディスク上にある場合、まずメモリにメモリを適用し、次にコンテンツとページ テーブルを入力する必要があります。マッピング関係を確立します。ページ テーブルにも列があります。コンテンツは、アドレスがメモリとメモリ内のコンテンツを割り当てているかどうかを判断するために特に使用されます。1 などの 2 ビットが含まれる場合があります。 /0 は、割り当てられたメモリの有無1/0を意味します。 a>を意味します。たとえば、次の図:メモリにコンテンツがあるかどうか は、

ここに画像の説明を挿入します

プロセスが物理アドレスを見つけるために仮想アドレスを取得するとき、この時点でメモリが物理アドレスを割り当てていないと仮定すると、オペレーティング システムはプロセスを一時停止し、対応するアドレスをディスクからロードします。対応するプログラムがメモリに保存され、その後、ページ テーブルが埋められてマッピング関係が確立されます。このプロセスは、 ページ フォールト割り込みと呼ばれます。この概念については後ほど紹介しますが、最初に理解しておきましょう。

上記のメモリにメモリを適用し、コンテンツとページ テーブルを入力し、マッピング関係を確立するとき、この一連のプロセスはメモリ管理と呼ばれます。 a>; プロセスはこのプロセスに注意を払う必要はなく、プロセスはこのプロセスを認識しません。プロセスが行う必要があるのは、スケジュールすべきものをスケジュールし、アクセスすべきものにアクセスすることだけです。このセットはプロセス管理と呼ばれます。プロセス管理はメモリ管理を考慮しませんので、プロセス管理とメモリ管理はアドレス空間とページ テーブルが存在するため、オペレーティング システム レベルで実現されます。モジュールの分離!

最後に、ページ テーブルを通じて、プロセスをさまざまな物理メモリにマッピングすることもできるため、プロセスの独立性を実現できます。

5. スペース割り当ての拡張

現在使用できるアドレス空間は、ユーザー空間で使用されています。アドレス空間には、オペレーティング システム自体のために予約されている空間の一部がまだあります。ユーザーが使用する空間はユーザー空間と呼ばれ、その容量は 3GB です。オペレーティング システム自体が使用する領域はカーネル領域と呼ばれ、1GB です。次の図に示すように:

ここに画像の説明を挿入します

struct mm_struct 構造体には、実際には vm_area_struct 構造体を指すポインタがあります。これは何に使用されますか?私たちのアドレス空間は多くのエリアに分割されていますが、まだ使用されていないエリアが常に存在するため、独自のエリアを分割したい場合は、1 つの vm_area_struct オブジェクト、このオブジェクトを申請できます。 2 つの値があります: startend はそれぞれ開始と終了です。空間の next ポインタもあり、次の構造のオブジェクトを指します。これらはリンク リスト構造を形成するため、With があります。このような構造なので、自分のニーズに応じて自分のスペースを分割できます。次の図に従って理解できます。

ここに画像の説明を挿入します

このうち、 mm_struct 構造は実際にはメモリ記述子と呼ばれ、 vm_area_struct は線形空間と呼ばれ、これら 2 つの概念を組み合わせてアドレス空間と呼ばれます。 !ただし、便宜上、 mm_struct がアドレス空間であると考えます。

おすすめ

転載: blog.csdn.net/YoungMLet/article/details/133945780