linuxサブプロセス親プロセスが終了したソリューションを知っています

6595 ワード

実際の開発ではプロセス間の関係を処理することは避けられないが,最も一般的なのは親子プロセスの相互監督である.親プロセスは、子プロセスを待つか、親プロセスが実行を終了するかどうかをプロセスから知ることで、リソースを解放できます.
一、プロセスについて
プロセスは、オペレーティングシステムがリソースの割り当てとスケジューリングを行う基本単位です.linuxシステムはforkを使用してプロセスを作成し、プロセスpid 0はswapperプロセス、プロセスpid 1はinitプロセス、initプロセスはすべての一般的なユーザープロセスの親プロセスです.
forkはファイルで以下のように定義されています.
pid_t fork(void);
forkが成功すると2回、親プロセスに1回、子プロセスに1回戻ります.
fork呼び出しに成功した場合、pid>0を親プロセスに、pid==0を子プロセスに返します.
fork呼び出しに失敗した場合、pid==1を親プロセスに返し、errnoを設定します.
二、親プロセスと子プロセスの関係
forkを通過すると、親プロセスはpid>0を返し、子プロセスはpid==0を返すのはなぜですか.これは、親プロセスが自分がどれだけのサブプロセスを持っているかを知ることができず、サブプロセスが作成されると親プロセスとは独立し、サブプロセスのidが分からないためです.サブプロセスはgetppidによって親プロセスのpidを取得できます.親プロセスがサブプロセスよりも早く終了すると、サブプロセスは孤児プロセスとなり、initプロセスppid==1にリセットされます.サブプロセスが早期に終了し、親プロセスがwait関数を使用して処理が終了したサブプロセスを傍受していない場合、サブプロセスはゾンビプロセスとなります.すなわち、システムはプロセスが存在すると考えています.プロセスpidなどのプロセス情報を占有し、システムがpid番号を再利用できなくなります.ゾンビプロセスは名実ともに存在するプロセスであり、システムはプロセスがまだ存在すると考えています.サブプロセスはすでに彼の実行タスクを完了しました.
三、簡単なプロセスルーチン
simple_process.cpp
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
    printf("start program %s, pid: %ld, ppid: %ld 
", argv[0], getpid(), getppid()); pid_t pid = fork(); if (-1 == pid) { printf("fork process failed. errno: %u, error: %s
", errno, strerror(errno)); exit(-1); } if (pid > 0) { // parent printf("parent precess
"); sleep(1); /** 、wait */ // int status; // wait(&status); } else { // child printf("child process, pid: %ld, ppid: %ld
", getpid(), getppid()); for (int i = 0; i < 5; i++) { printf("child sleep %ds
", i); sleep(1); } printf("##child process, pid: %ld, ppid: %ld
", getpid(), getppid()); } return 0; }

コンパイル運転
$ g++ simple_process.cpp
$ ./a.out
start program ./a.out, pid: 17164, ppid: 8564
parent precess
child process, pid: 17165, ppid: 17164
child sleep 0s
[sunny@icentos process-wait]$ child sleep 1s
child sleep 2s
child sleep 3s
child sleep 4s
##child process, pid: 17165, ppid: 1
親プロセスが早期に終了すると、サブプロセスの親進臣はinitプロセス、ppid==1に変化することがわかります.
四、親プロセスの傍受を実現する子プロセスの実行終了
「フラグ1」のwaitコードをコメントを削除すると、
次のコードを使用します.
     /**    、wait         */
     int status;
     wait(&status);

コンパイル
$ g++ simple_process.cpp
$ ./a.out
start program ./a.out, pid: 28989, ppid: 8564
parent precess
child process, pid: 28990, ppid: 28989
child sleep 0s
child sleep 1s
child sleep 2s
child sleep 3s
child sleep 4s
##child process, pid: 28990, ppid: 28989
wait関数は、サブプロセスが終了するのを待ってサブプロセスが終了した状態を取得する役割を果たすため、親プロセスwaitはサブプロセスが終了するのを待ってから終了を実行し続けるため、プロセスからのppidは元のfork親プロセスのpidである.wait関数はブロックされているため,実際の開発ではサブプロセスの終了を傍受する必要があり,新しいスレッドを作成して傍受し,コールバック関数で処理することができる.
五、子プロセスの親プロセスの傍受の実行終了を実現する
fork作成プロセス以降、サブプロセスは独立した実行単位(プロセスはオペレーティングシステムのリソース割り当てとスケジューリングの基本単位)となります.サブプロセスは親プロセスの元の変数を継承しますが、しかし、サブプロセスのこれらの変数に対する操作は、対応する親プロセス変数の値などに影響しません(ここでは、単純なint、long変数を指し、socket、fileなどの共有リソースであれば影響する可能性があります).サブプロセスは、親プロセスの実行ステータス(実行中か実行終了か、プロセスが生存しているか)を通常の方法で知ることができません.
サブプロセスは親プロセスの状態がプロセス通信方法でしか解決できないことを知っておく必要があります.よく使われるプロセス間通信:匿名パイプ、有名パイプ、信号量、メッセージキュー、信号、共有メモリ、ソケット.
本稿では,ソケットを用いて親プロセスが自動的にサブプロセスを終了する方法を紹介し,主に2つの特性を用いた.
1)socketpairは双方向のハンドルを生成し,2つのハンドルは通信の両端に相当し,プログラムは一端で書き込み,他端でコンテンツを読み取ることができる.
2)プロセス実行が終了すると、システムは自動的にリソースを回収し、そのプロセスが占有するファイルハンドルを閉じ、ソケットハンドルもハンドルである.
コード実装
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void * thr_child(void *arg) {
    int sock = *((int*)arg);
    char buf[2] = {0};
    ssize_t len = 0;
    while (-1 == (len = read(sock, buf, sizeof(buf))) && EINTR == errno);
    printf("thr_child. pid: %ld, ppid:%ld
", getpid(), getppid()); exit(-1 == len ? -1 : 0); } int main(int argc, char *argv[]) { printf("start program: %s, pid: %ld, ppid: %ld
", argv[0], getpid(), getppid()); int fd[2] = {0}; if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) { perror("socketpair failed
"); exit(-1); } pid_t pid = fork(); if (-1 == pid) { perror("fork failed
"); exit(-1); } if (0 == pid) { // child printf("child process, pid: %ld, ppid: %ld
", getpid(), getppid()); close(fd[0]); int sock = fd[1]; pthread_t pid; if (pthread_create(&pid, NULL, thr_child, (void *)&sock)) { perror("child. create thread failed
"); exit(-1); } for (int i = 0; i < 20; i++) { sleep(1); printf("child sleep: %ds
", i + 1); } } else { // parent printf("parent process, pid: %ld, ppid: %ld
", getpid(), getppid()); close(fd[1]); int sock = fd[0]; for (int i = 0; i < 5; i++) { sleep(1); printf("parent sleep: %ds
", i + 1); } } printf("process %s finish
", pid > 0 ? "parent" : "child"); return 0; }

コンパイルおよび実行
$ g++ -pthread notify_parent_quit.cpp
$ ./a.out
start program: ./a.out, pid: 31141, ppid: 8564
parent process, pid: 31141, ppid: 8564
child process, pid: 31142, ppid: 31141
parent sleep: 1s
child sleep: 1s
parent sleep: 2s
child sleep: 2s
parent sleep: 3s
child sleep: 3s
parent sleep: 4s
child sleep: 4s
parent sleep: 5s
process parent finish
child sleep: 5s
thr_child. pid: 31142, ppid:1
wait関数を使用すると、親プロセスはサブプロセスsleepが20秒待ってから自分で終了することがわかります.ただし、親プロセスが終了するとソケットハンドルsockが閉じられるため、サブプロセスreadは0を返してファイルの最後に達することを示します.親プロセスにはアクティブなcloseソケットハンドルsockがないため、親プロセスが終了するとオペレーティングシステムによってハンドルが閉じられます.上のnotifyプログラムは,この点を用いて親プロセスの終了時にサブプロセスに通知する.スレッド関数thr_childは、スレッドが終了する前にppidを印刷すると、サブプロセスの親プロセスppid==1、すなわち、親プロセスが終了することによってサブプロセスが孤児プロセスになることを知ることができる.
まとめ、socketpairは単純な実現方法の一つにすぎない.ここでは単純な例を用いて説明するだけで,実際の応用で処理する場合は複雑である.