3-2进程退出

进程退出

一个程序启动之后,变成了一个进程,在以下情况会退出

  1. 运行到最后一行语句
  2. 运行时遇到 return
  3. 运行时遇到 exit 函数
  4. 程序异常
  5. 进程接收到中断信号

一个进程要么是正常,要么是异常结束,无论怎么退出,进程都会返回一个终止状态码,进程结束时不会真的退出,还会驻留在内存中,父进程可以使用 wait 函数来获取进程的终止状态码同时该函数会释放进程的内存空间

######## 僵尸进程

子进程已经结束,父进程还没有使用 wait/wait_pid 来回收(无人收尸)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid == -1) {
    //错误处理:创建子进程失败时返回-1.
    die('could not fork');
} else if ($pid) {
    fprintf(STDOUT,"i am father %s\n",posix_getpid());

    while(true){
    }
    // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    // pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
} else {
    fprintf(STDOUT,"i am child %s\n",posix_getpid());
    die('i am child bye');
    //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
}

僵尸进程占用的内存空间并未被释放,我们可以到 /proc/pid 目录查看

一个进程运行时,会把进程的信息写入到 /proc/ 目录下,目录名为 pid 号

回收子进程:

PHP 使用 pcntl_wait 函数 https://www.php.net/manual/zh/function.pcntl-wait.php

  1. 如果没有子进程,可能会返回错误

  2. 如果子进程还没结束,会阻塞父进程

  3. 使用参数可以以非阻塞的方式回收

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 阻塞方式
$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid == -1) {
    //错误处理:创建子进程失败时返回-1.
    die('could not fork');
} else if ($pid) {
    fprintf(STDOUT,"i am father %s\n",posix_getpid());
    //父进程会得到子进程号,所以这里是父进程执行的逻辑
    $exitPid = pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
    if($exitPid > 0){
        fprintf(STDOUT,"子进程 %d 已回收,终止状态码 %d\n",$pid,$status);
    }else{
        fprintf(STDOUT,"wait error\n");
    }
} else {
    fprintf(STDOUT,"i am child %s\n",posix_getpid());
    while(true){

    }
    //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
## 费阻塞方式
$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid == -1) {
    //错误处理:创建子进程失败时返回-1.
    die('could not fork');
} else if ($pid) {
    fprintf(STDOUT,"i am father %s\n",posix_getpid());
    while (true){
        //父进程会得到子进程号,所以这里是父进程执行的逻辑
        $exitPid = pcntl_wait($status,WNOHANG); //等待子进程中断,防止子进程成为僵尸进程。
        if($exitPid > 0){
            fprintf(STDOUT,"子进程 %d 已回收,终止状态码 %d\n",$pid,pcntl_wexitstatus($status));
            break;
        }
        fprintf(STDOUT,"wait ing\n");
    }

} else {
    fprintf(STDOUT,"i am child %s\n",posix_getpid());
    exit(10);
    //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
}

######## 使用信号中断程序

如果是程序执行完成,或者使用 return exit 退出,使用 **pcntl_wexitstatus() **函数来获取退出编号,并且可以使用 **pcntl_wifexited() **函数来判断是否完成退出

如果使用中断信号中断程序,那么就要使用pcntl_wifsignaled()函数来判断,或使用pcntl_wifstopped() 函数来获取发送 SIGSTIO SIGTSTP 信号终止的程序退出码

对于不同的中断信号,使用不同的函数进行判断,但是只要退出,**pcntl_wait() **就会返回正常的值