【Linux】プロセスの概念
記事ディレクトリ
プロセス定義
プロセスはプログラムの実行インスタンスであり、システム リソース (CPU 時間、メモリ) の割り当てを担当するエンティティです。
プロセスとプログラムの関係
プログラミング言語で書かれたコードによって形成されたコンパイルされたバイナリ プログラムはハードディスクに保存され、コンピュータがプログラムを起動すると、プログラムの関連コードとデータが CPU で使用するためにメモリにロードされます。
プログラム コードとデータがメモリにロードされた後、オペレーティング システムはプログラムを管理する必要があります。これらのプログラムをより適切に管理するには、まず、これらのプログラムを記述するための対応する構造を作成する必要があります。オペレーティング システムでは、プログラムの構造を記述します。プロセス コントロール ブロック (略して PCB) と呼ばれます。Linux システムでは PCB は task_struct と呼ばれます。対応するコードとデータのアドレスも PCB に記録されます。これらにアクセスしやすくするために、 PCB、それらを組織するためにチェーン構造が使用されます。:
task_ struct の内容は次のように分類されます。
- 識別子: このプロセスを説明し、他のプロセスを区別するために使用される一意の識別子。
- ステータス: タスクのステータス、終了コード、終了信号など。
- 優先度: 他のプロセスとの相対的な優先度。
- プログラム カウンター: プログラム内で実行される次の命令のアドレス。
- メモリ ポインタ: プログラム コードおよびプロセス関連データへのポインタ、および他のプロセスと共有されるメモリ ブロックへのポインタを含む
- コンテキストデータ: プロセス実行時のプロセッサのレジスタ内のデータ[休職の例、CPU、レジスタの図を追加してください]。
- I/O ステータス情報: 表示される I/O リクエスト、プロセスに割り当てられた I/O デバイス、プロセスで使用されるファイルのリストが含まれます。
- 会計情報: 合計プロセッサ時間、使用されるクロックの合計数、制限時間、会計アカウントなどが含まれる場合があります。
- その他の情報。
プログラムのコードとデータをメモリにロードするだけで、オペレーティング システムが管理用の PCB を作成しない場合、オペレーティング システムはスケジュールを設定せず、プログラムの実行を完了できません。 、プロセスの本質はメモリ内のコードとデータです. データ + プロセス制御ブロック. PCB を使用すると、オペレーティング システムはプロセスの管理を PCB の管理に変換します。コードとデータ:
Linux でのプロセスを確認する
Linux オペレーティング システムでプロセスをよりよく表示するには、ソース ファイル myprocess.c とバイナリ プログラムを作成するための makefile ファイルを作成します。
ソースファイルの内容は以下のとおりです。
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello myprocess\n");
sleep(1);
}
return 0;
}
メイクファイルの内容は以下のとおりです。
myprocess:myprocess.c
gcc -o myprocess myprocess.c
.PHONY:clean
clean:
rm -f myprocess
上記のファイルを作成し、コンパイルして myprocess という名前のバイナリ プログラムを取得し、Linux で 2 つのクライアントを起動すると、起動プログラムの 1 つがプロセスになります。
別のクライアントを入力してps axj | head -1 && ps axj | grep myprocess | grep -v grep
、myprocess プロセスを表示します。
上記はプロセスを表示するための命令の使用です。命令は次のとおりです。
ps axj | head -1 && ps axj | grep 进程名 | grep -v 进程名
さらに、/proc ディレクトリ内のプロセスも確認できます。
/proc ディレクトリは、ハードディスク上には存在しないメモリ レベルのディレクトリです。pid と同じ名前のディレクトリが存在します。対応するプロセスの task_struct は、このディレクトリに記録されます。プロセスが対応するディレクトリを閉じると、 、削除されます。
Linux でシステムコールを通じてプロセス識別子を取得する
プロセスを一意に識別するために、Linux オペレーティング システムは、プロセスごとに PCB にプロセス識別子 (pid) を設定します。また、現在のプロセスの pid を取得するためのシステム インターフェイス関数 getpid も提供します。その概要は次のとおりです。
getpid 関数をテストするには、ソース ファイル myprocess.c を変更します。内容は次のとおりです。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello myprocess, 我的pid是%d\n", getpid());
sleep(1);
}
return 0;
}
コマンドを使用してプロセス PID をクエリし、プロセスの実行結果を表示します。
さらに、Linux オペレーティング システムは、現在のプロセスの親プロセスの PID、つまり ppid を記録するために使用される親プロセス識別子も設定します。また、現在のプロセスの ppid を取得する getppid 関数も提供します。 ppid の値は次のとおりです。
getppid 関数をテストするには、ソース ファイル myprocess.c を次のように変更します。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello myprocess, 我的pid是%d, 我的ppid为%d\n", getpid(), getppid());
sleep(1);
}
return 0;
}
コマンドを使用してプロセス PID をクエリし、プロセスの実行結果を表示します。
プロセスを複数回閉じてctrl+c
から再起動することを活用してください。
プロセスの pid がどのように変化しても、プロセスの ppid は変化しないことがわかります。次のコマンドを使用して親プロセスを表示してみます。
実際、この親プロセスは bash であり、上記の現象を通じて次の結論を導き出すことができます。
- コマンド ライン インタープリター (bash) は本質的にプロセスです。
- コマンドラインから開始されたすべてのプログラムは最終的にプロセスになり、このプロセスの対応する親プロセスは bash です。
Linux でのシステムコールによるプロセスの作成 - fork 関数の使用
fork 関数は、子プロセスを作成するために Linux システムによって提供されるシステム コールです。
- fork 関数が正常に実行されると、実行フローは 2 つになります。1 つは fork 関数を呼び出す親プロセス、もう 1 つは fork 関数によって作成された子プロセスです。
- 作成された子プロセスは親プロセスのコードとデータを親プロセスと共有し、親プロセスのフォーク関数が子プロセスを作成した後に子プロセスがコードを実行します。
- fork 関数は、子プロセスの pid を親プロセスに返し、作成された子プロセスには 0 を返し、エラーの場合は -1 を返します。
フォーク関数をテストするには、ソース ファイル myprocess.c を次の内容で変更します。
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
//子进程
printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
sleep(2);
}
else if (id > 0)
{
//父进程
printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
sleep(3);
}
else
{
//fork函数出错
assert(1);
}
return 0;
}
例証します:
- fork 関数に必要なヘッダー ファイルは次のとおりです
unistd.h
。 - 条件判断を使用して、親プロセスと子プロセスが異なるコードを実行するように制御します。
コマンドを使用してプロセスをクエリし、プロセスの実行結果を表示します。
フォーク機能の原理
プロセスの本質は、PCB + メモリ上のコードとデータです。fork 関数によって作成された子プロセスは、親プロセスとコードとデータを共有するため、fork 関数による子プロセスの作成の原則は、メモリ上の PCB を作成することです。子プロセス PCB の大部分のデータは親プロセスと同じであり、同じコードとデータを指します。
フォークでのプロセスの独立性の具体化
まず、次の定理が与えられます。プロセスは互いに独立しており、あるプロセスの操作は他のプロセスに影響を与えません。
fork 関数を使用して子プロセスを作成する場合にも、プロセス間の独立性が保証されます。独立性を確認するには、ソース ファイル myprocess.c を次の内容に変更します。
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
//子进程
printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
sleep(20);
printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
printf("我是子进程,我已经关闭了\n");
}
else if (id > 0)
{
//父进程
printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());
sleep(3);
printf("我是父进程,我已经关闭了\n");
}
else
{
//fork函数出错
assert(1);
}
return 0;
}
コマンドを使用してプロセスをクエリし、プロセスの実行結果を表示します。
最初に、親プロセスと子プロセスが一緒に実行されます。
親プロセスはシャットダウンされ、子プロセスは通常どおり実行されます。
最後に、子プロセスが閉じられます。
上記のテストから、親プロセスのシャットダウンは子プロセスの通常の実行に影響を与えず、ある程度の独立性が確保されていることがわかります。さらに、コードは読み取り専用であるため、親プロセスはコードを変更して子プロセスに影響を与えることができず、データ変更によりコピーオンライトメカニズムがトリガーされ、ある程度の独立性が確保されます。
コピーオンライト現象を観察するには、ソース ファイル myprocess.c を次のように変更します。
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
int main()
{
int a = 0;
pid_t id = fork();
if (id == 0)
{
//子进程
printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
sleep(5);
printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
}
else if (id > 0)
{
//父进程
printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
a = 666;
printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);
sleep(3);
printf("我是父进程,我已经关闭\n");
}
else
{
//fork函数出错
assert(1);
}
return 0;
}
プロセスの実行結果を表示します。
この現象を観察すると、親プロセスが a の値を変更した後、子プロセスの a の値は変更されませんが、親プロセスと子プロセスの a 変数のアドレスは同じであることがわかります。コピーオンライトによって引き起こされる現象です。
2 つの戻り値を返す fork 関数の原理
fork によって作成された子プロセスは親プロセスとコードとデータを共有し、fork 関数も親プロセスのコードの一部であるため、親プロセスが子プロセスの作成を完了した後、子プロセスも子プロセスを実行します。 fork 関数を使用して、子プロセスの残りのコードを作成します。これには fork 関数の戻り部分が含まれるため、親プロセスは戻り部分を実行し、子プロセスも戻り部分を実行するため、fork 関数は 2 つの戻り値を返します: