プロセス待機、プロセス交換

目次

プロセス待機中

waitpid関数

待機機能

プロセスの置き換え


プロセス待機中

プロセス待ちの意味

子プロセスが終了した場合、親プロセスがそれを無視すると、「ゾンビ プロセス」問題が発生し、メモリ リークが発生する可能性があります。さらに、プロセスがゾンビになると、そのプロセスは無敵になり、死んだプロセスを誰も強制終了できないため、「まばたきせずに強制終了」の kill -9 は何もできません。最後に、親プロセスによって子プロセスに割り当てられたタスクがどのように完了するかを知る必要があります。たとえば、子プロセスの実行が終了したかどうか、結果が正しいかどうか、正常に終了したかどうかなどです。親プロセスは、子プロセスのリソースを再利用し、プロセス待機によって子プロセスの終了情報を取得する必要があります。

waitpid関数

#include <sys/types.h>

#include <sys/wait.h>

pid_ t waitpid(pid_t pid, int *status, int options);

戻り値:

  • 正常に戻る場合、waitpid は収集された子プロセスのプロセス PID を返します。
  • オプション WNOHANG が設定されており、呼び出し中に収集する終了した子プロセスが存在しないことが waitpid によって検出された場合は、0 を返します。
  • 呼び出しにエラーがある場合は、-1 が返され、errno にはエラーを示す対応する値が設定されます。

パラメータ:

1. pid (待機セットのメンバーを決定するため) :

pid=-1 の場合、待機セットは親プロセスが所有する子プロセスで構成されます。待つのと同じ。

pid > 0 の場合、待機セットはプロセス ID が pid と等しい単一の子プロセスになります。

2. ステータス (デフォルトの動作を変更) :

status パラメータが null 以外の場合、waitpid は戻りの原因となった子プロセスのステータスに関するステータス情報を入力します。wait.h ヘッダー ファイルは、ステータス パラメーターを解釈するいくつかのマクロを定義します。

  • WIFEXITED(status): exit または return (return) を呼び出して子プロセスが正常に終了した場合に true を返します。
  • WEXITSTATUS(status): 正常に終了した子プロセスの終了ステータスを返します。このステータスは、WIFEXITED() が true を返した場合にのみ定義されます。
  • WIFSIGNALED(status) : シグナルがキャッチされなかったために子プロセスが終了した場合は true を返します。
  • WTERMSIG( status): 子プロセスを終了させる原因となったシグナルの番号を返します。この状態は、WIFSIGNALED() が true を返した場合にのみ定義されます。
  • WIFSTOPPED(status): 戻りの原因となった子プロセスが現在停止している場合は true を返します。
  • WSTOPSIG(status): 子プロセスを停止させる原因となったシグナルの番号を返します。この状態は、WIFSTOPPED() が true を返した場合にのみ定義されます。
  • WIFCONTINUED(status): 子プロセスが再起動する SIGCONT シグナルを受信した場合は true を返します。

呼び出しプロセスに子がない場合、waitpid は -1 を返し、errno を ECHILD に設定します。waitpid 関数がシグナルによって中断されると、-1 を返し、errno を EINTR に設定します。

3. オプション (リサイクルされた子プロセスの終了ステータスを確認します) :

デフォルトの動作は、定数 WNOHANG、WUNTRACED、WCONTINUED のさまざまな組み合わせにオプションを設定することで変更できます。

  • WNOHANG: 待機セット内の子プロセスが終了していない場合は、すぐに戻ります (戻り値は 0)。デフォルトの動作では、子プロセスが終了するまで呼び出しプロセスを一時停止します。このオプションは、子プロセスが終了するのを待っている間に有用な作業を実行したい場合に便利です。
  • WUNJTRACED: 待機セット内のいずれかのプロセスが終了するか停止されるまで、呼び出し元プロセスの実行を一時停止します。返される PID は、リターンの原因となった終了または停止された子プロセスの PID です。デフォルトの動作では、終了した子プロセスのみを返します。このオプションは、終了した子プロセスと停止した子プロセスを検査する場合に便利です。
  • WCONTINUED: 待機セット内の実行中のプロセスが終了するか、待機セット内の停止したプロセスが実行を再開する SIGCONT シグナルを受信するまで、呼び出し元プロセスの実行を一時停止します。

    これらのオプションは、OR 演算を使用して組み合わせることができます。例えば:

  • WNOHANG | WUNTRACED: 待機セット内の子プロセスがどれも停止または終了していない場合、戻り値は 0 であり、1 つが停止または終了している場合、戻り値は子プロセスの PID です。

待機機能

wait 関数は、waitpid 関数の簡易バージョンです。

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int*status);  

戻り値: 待機中のプロセスの PID が正常に返され、失敗した場合は -1 が返されます。

パラメータ: 出力パラメータ。子プロセスの終了ステータスを取得します。気にしない場合は、NULL に設定できます。

wait(&status) を呼び出すことは、waitpid(-1,&status,0) を呼び出すことと同じです。

子プロセスがすでに終了している場合、wait/waitpid を呼び出すと、wait/waitpid はすぐに戻り、リソースを解放し、子プロセスの終了情報を取得します。

wait/waitpid が呼び出され、子プロセスが存在し、正常に実行されている場合、プロセスはブロックされる可能性があります。

サブプロセスが存在しない場合は、すぐにエラーが返されます。

親プロセスは子の PID を順番に保存し、最初の引数として適切な PID を指定して waitpid を呼び出すことによって、同じ順序で各子を待機します。 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 #define N 2
int main()
{                                                                                                                                                                                           
  int status,i;
   pid_t pid[N],retpid;
    //Parent creates N children
   for(i=0;i<N;i++)
      if((pid[i] = fork()) == 0)//child
       exit(100+i);

     //Parent reaps N children in order
     i=0;
    while((retpid = waitpid(pid[i++],&status,0)) > 0)
    {
         if(WIFEXITED(status))
           printf("child %d terminated normally with exit status=%d\n",retpid,WEXITSTATUS(status));
         else 
           printf("child %d terminated abnormally\n",retpid);
     }
 
     the only normal termination is if there are no more children
    if(errno != ECHILD)
      unix_error("waitpid error");
  
     exit(0);                                                       
 } 

子プロセスが終了しない場合、親プロセスは常に子プロセスが終了するのを待ちます。待機中は親プロセスは何もできません。このような待ちをブロッキング待ちといいます。

実際、子プロセスの終了を待機している間に親プロセスに独自の処理の一部を実行させ、子プロセスの終了時に子プロセスの終了情報を読み取ることができます (ノンブロッキング待機)。

現時点では、optionsパラメータを使用できます

WNOHANG : 待機セット内の子プロセスが終了していない場合は、すぐに戻ります (戻り値は 0)。デフォルトの動作では、子プロセスが終了するまで呼び出しプロセスを一時停止します。このオプションは、子プロセスが終了するのを待っている間に有用な作業を実行したい場合に便利です。

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
	pid_t pid;
	pid = fork();
	if (pid < 0) {
		printf("%s fork error\n", __FUNCTION__);
		return 1;
	}
	else if (pid == 0) { //child
		printf("child is run, pid is : %d\n", getpid());
		sleep(5);
		exit(1);
	}
	else {
		int status = 0;
		pid_t ret = 0;
		do
		{
			ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
			if (ret == 0) {
				printf("child is running\n");
			}
			sleep(1);
		} while (ret == 0);
		if (WIFEXITED(status) && ret == pid) {
			printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));
		}
		else {
			printf("wait child failed, return.\n");
			return 1;
		}
	}
	return 0;
}

プロセスの置き換え

exec はプログラム置換関数であり、プロセス自体を作成しません。exec によって生成されるプロセスは、現在のプロセスの同一のコピーであり、pid は変更されていません。

置換関数

int execve(const char *path, char *const argv[], char *const envp[]);

成功した場合は何も返しません。エラーの場合は -1 を返します。 

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ...,char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

実際のシステムコールは execve のみで、他の関数は execve 関数をカプセル化し、最後に execve を呼び出します。

これらの関数プロトタイプは混同しやすいように見えますが、ルールをマスターしていれば簡単に覚えることができます。

l(list) : パラメータがリストを採用することを示します。

v(vector) : パラメータの配列。

p(path): 環境変数 PATH を自動的に検索するための p があります。

e(env): 環境変数を独自に保持することを示します 

execl: パラメータの形式はリストであり、パスはありません。現在の環境変数を使用します。

execlp: パラメータの形式はパスを含むリストであり、環境変数を自分で組み立てる必要があります。

execle: パラメーターの形式はリストであり、パスはありません。現在の環境変数を使用します。

execv: パラメータの形式は配列であり、パスはなく、現在の環境変数を使用します。

execvp: パラメータの形式はパスを含む配列であり、環境変数を自分で組み立てる必要があります。

プロセスが exec 関数を呼び出すと、プロセスのユーザー空間コードとデータは完全に新しいプログラムに置き換えられ、実行はそのプログラムのスタートアップ ルーチンから開始されます。新しいプログラム。次のコードは古いコードに属しており、直接置き換えられるため、再度実行されることはありません。 

 

#include <unistd.h>

int main()

{

        char *const argv[] = {"ls", "-l", NULL};

        char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

        execl("/bin/ls", "ls", "-l", NULL); // p を指定すると、環境変数 PATH を使用でき、フルパスを記述する必要はありません execlp("ls", "ls" , "-l ", NULL); // e の場合は、環境変数を自分で組み立てる必要があります

        execle("ls", "ls", "-l", NULL, envp);

        execv("/bin/ls", argv); // p を指定すると、環境変数 PATH を使用できるため、フルパスを記述する必要はありません

        execvp("ls", argv); // eの場合は環境変数を自分で組み立てる必要がある

        execve("/bin/ls", argv, envp);

        終了(0);

}  

 

 配列の最後の要素は空であることが望ましい。

 

おすすめ

転載: blog.csdn.net/m0_55752775/article/details/130296320