Linuxプロセスプログラミングプラクティス1-プロセスの基本概念、プロセスを作成するためのフォーク

1.プロセスの基本概念

プロセスを学ぶ前に、プログラムとプロセスという2つの概念を理解して区別する必要があります。

1.1プログラムVSプロセス

<1>プログラムとは?

プログラムは、特定のタスクを完了するための一連の指示です。
たとえば、次のコードはプログラムです

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

int main()
{
    
    
  while(1)
  {
    
    
  	printf("hello world\n");
    sleep(1);
  }
  return 0;
}

コンパイルしたら、次のコマンドを実行します。
ここに画像の説明を挿入します

<2>プロセスとは何ですか?

  • からユーザー観点から:プロセスはプログラムの実行プロセスです
  • からオペレーティング・システムコアの観点:プロセスは、オペレーティングシステムによって割り当てられたメモリやCPUタイムスライスなどのリソースの基本単位です。

プロセスはリソース割り当ての最小単位あり、各プロセスには独自の独立したアドレス空間実行ステータスがありますLinuxのようなマルチタスクオペレーティングシステムでは、多くのプログラムを同時に実行でき、実行中の各プログラムがプロセスを構成します
別のウィンドウを開き、psコマンドを使用して、実行中のプログラムとそれが構成するプロセスを表示します。
ここに画像の説明を挿入します

<3>プロセスとプログラムの違い

  • プロセスは動的であり、プログラムはまだあります
  • プロセスのライフサイクルは比較的短いですが、プログラムは永続的です。
  • 1つのプロセスは1つのプログラムにのみ対応でき、1つのプログラムは複数のプロセスに対応できます

1.2プロセスデータ構造(記述プロセス)

<1>オペレーティングシステムのプロセスの説明-PCB

プロセスの静的記述は、PCB、関連するプログラムセグメント、およびプログラムセグメントが動作するデータ構造セットの3つの部分で構成されます。

  • プロセス制御ブロック(プロセス制御ブロック、PCB):プロセスを記述し、プロセスの操作に必要なすべての情報を制御するために使用されます。
  • コードセグメント:プロセス内のプロセススケジューラがCPU上で実行できるプログラムコードセグメントです。
  • データセグメント:プロセスのデータセグメント。これは、プロセスに対応するプログラムによって処理された元のデータ、またはプログラムの実行後に生成された中間データまたは最終データの場合があります。

<2> Linuxでのプロセスの説明-task_struct

  • Linuxでのプロセスを記述する構造体は、task_structと呼ばれます。それは一種のPCBです。
  • task_structはLinuxカーネルのデータ構造であり、RAM(メモリ)にロードされ、プロセスの情報が含まれます

task_structコンテンツ分類

  • 識別子:このプロセスを説明する一意の識別子で、他のプロセスを区別するために使用されます。
  • ステータス:タスクステータス、終了コード、終了シグナルなど。
  • 優先度:他のプロセスと比較した優先度。
  • プログラムカウンタ:プログラムで実行される次の命令のアドレス。
  • メモリポインタ:プログラムコードとプロセス関連データへのポインタ、および他のプロセスと共有されるメモリブロックへのポインタが含まれます
  • コンテキストデータ:プロセスの実行中のプロセッサのレジスタ内のデータ。
  • I / Oステータス情報:表示されたI / O要求、プロセスに割り当てられたI / Oデバイス、およびプロセスで使用されるファイルリストを含みます。
  • アカウンティング情報:合計プロセッサ時間、使用されているクロックの合計数、制限時間、アカウント番号などが含まれる場合があります。
  • その他の情報

2、フォーク作成プロセス

2.1プロセス識別子を取得してプロセスを表示する

プロセスの最もよく知られている属性は、プロセスID(processID、PID)とその親プロセスID(親processID、PPID)です。

  • PIDとPPIDはどちらもゼロ以外の整数です。
  • PIDはプロセスを一意に識別します。
  • 1つのプロセスによって作成された別の新しいプロセスは、子プロセスと呼ばれます逆に、子プロセスを作成するプロセスは、親プロセスと呼ばれます
  • すべてのプロセスは、その祖先が最終的に本体のプロセスのパスNo. 1に分類されることをトレースします。これは、initプロセスと呼ばれるプロセスです。initプロセスは、Linuxカーネルの実行を開始した後の最初のプロセスです。
  • Initはシステムを起動し、デーモンを起動して、必要なプログラムを実行します。

getpid()取得プロセスIDを呼び出すことによって、呼び出すためにgetppid()例えば、親プロセスIDを取得します:

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

int main()
{
    
    
  printf("子进程pid:%d\n",getpid());
  printf("父进程ppid:%d\n",getppid());

  return 0;
}

操作結果:
ここに画像の説明を挿入します
ps、top、その他のコマンドを使用して、システムプロセスを表示したり、次の操作を実行したりできます。
ここに画像の説明を挿入します

Q1:プロセスのPIDとその親プロセスのPIDを知る必要があるのはなぜですか?

PIDの一般的な使用法の1つは、一意のファイル名またはディレクトリ名を作成することです。
もう1つの使用法は、ログメッセージの一部としてPIDをログファイルに書き込むことです。

2.2フォーク作成プロセス

Linuxシステムの場合、man forkコマンドを実行してフォークを認識します
ここに画像の説明を挿入します

  • ヘッダーファイル<unistd.h>をインクルードします
  • 関数関数:子プロセスを作成します
  • 関数プロトタイプ:pid_t fork(void);
  • パラメータ:パラメータなし。
  • 戻り値:
    • 子プロセスが正常に作成されると、親プロセスの子プロセスIDが返されます
    • 子プロセスが正常に作成された場合、子プロセスの戻り値は0です。
    • -1の場合、作成に失敗しました
      ここに画像の説明を挿入します

関数の使用法を習得した後、例を使用してフォーク関数とプロセスの作成を習得して理解します

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

int main()
{
    
    
  printf("未调用fork打印一遍\n");
  
  fork();
  printf("调用fork打印两遍,父进程ID:%d , 进程ID:%d \n",getppid(), getpid());
  sleep(1);
  return 0;
}

上記のコードから、fork関数を呼び出してプロセスを作成すると、printfステートメントがfork()ステートメントの後に2回実行され
ここに画像の説明を挿入します
ていることがわかります。プロセス間の関係をより鮮明に理解できます。

bashプロセス(id:23920)-おじいちゃん

bashプロセスと親子関係にある最初の出力プロセス(id:31987)-son

2番目の出力プロセス(id:31988)は、最初の出力プロセス-孫と親子関係にあります。

Q1:プロセス作成を理解するにはどうすればよいですか?

ブログのセクション1.2で、プロセスのデータ構造がPCB +コードセグメント+データセグメント構成され、LinuxのPCBがtask_structに置き換えられていることが紹介されました。Linuxでプロセスを作成するには、必要であると考えることができます。現在3つの部分。

  • task_struct
  • データセグメント
  • コードセグメント
    ただし、子プロセス作成するには、task_structとデータセグメントを作成するだけで済みます。コードセグメントは親プロセスと共有されるため、特定のプログラムのスペースを節約できます。
    ここに画像の説明を挿入します

Q2:親プロセスと子プロセスの実行順序を理解するにはどうすればよいですか?

forkシステムコールの後、親プロセスと子プロセスが交互に実行されます。

Q3:フォークが1回呼び出して、2回(2つの戻り値)を返すことを理解するにはどうすればよいですか?

まず第一に、問題の本質は次のとおりです。2つのリターンがそれぞれのプロセススペースに返されます
Q1では、子プロセスと親プロセスがそれぞれ独自のメモリ空間(フォーク機能:コードセグメント、データセグメント、スタックセグメント、PCBプロセス制御ブロックのコピー)を持っていることを学びました親プロセスがfork関数を呼び出し、子プロセスもそれを呼び出し、戻り値が独自のデータセグメントに格納されます。プログラムを使用して、2つの戻り値を深く理解します。

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

int main()
{
    
    
  
  int ret =  fork();
  if(ret < 0)
  {
    
    
    printf("进程创建失败!\n");
  }
  else if(0 == ret)
  {
    
    
    printf("我是儿子(id:%d),fork返回值:%d\n",getpid(),ret);
  }
  else
  {
    
    
    printf("我是父亲(id:%d),fork返回值:%d\n", getpid(),ret);
  }
  sleep(1);
  return 0;
}

実行結果結果
ここに画像の説明を挿入します
から、子プロセスが正常に作成されたことがわかります親プロセスの場合、子プロセスID:4451が返され、子プロセスの戻り値は0です。

おすすめ

転載: blog.csdn.net/qq_40076022/article/details/113867381