上篇文章我们说到父进程应该回收子进程结束之后产生的数据,这样才会不浪费系统资源。
一个程序启动之后,变成了一个进程,进程在以下情况会退出
1)运行到最后一行语句
2) 运行时遇到return 时
3) 运行时遇到exit()函数的时候
4) 程序异常的时候
5) 进程接收到中断信号 
这篇我们认识一个新的函数
pcntl_wait 进程的退出
PHP: pcntl_wait - Manual
pcntl_wait — 等待或返回 fork 的子进程状态
wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。如果一个子进程在调用此函数时已经退出( 俗称僵尸进程 ),此函数立刻返回。子进程使用的所有系统资源将被释放。
意思就是说pcntl_wait默认是阻塞状态等待子进程结束,如果子进程没有通过wait回收的话就会变成一个僵尸进程占用资源

僵尸进程
演示僵尸进程前,先来能识别什么是僵尸进程
# linux命令行
ps -aux  
# 筛选php的进程
ps -aux | grep php  
# 进程状态  对应下图红线处
D 不可中断 Uninterruptible(usually IO)
R 运行状态,正在运行
S 处于休眠状态,是可被系统唤醒
T 停止状态
Z 僵尸进程
W 进入内存交换(从内核2.6开始无效)
X 死掉的进程

演示僵尸进程:
$pid = pcntl_fork(); // 开启子进程
if ($pid == 0){
    // 子进程运行结束 并没有被父进程回收的进程 就是僵尸进程
    echo "我是子进程,我的标识是:".posix_getpid().",好了我的任务结束了,拜拜"."\n";
}else{
    echo "我是父进程,我的标识是:".posix_getpid()."\n";
    // 父进程有任务还在运行的时候子进程已经结束了 这个时候子进程的垃圾并未被回收 还在占用这系统资源
    while (1){
        ;
    }
}

# 三种查看方式
pstree -ap | grep php
ps -ef | grep php
ps -aux | grep php
 

# /proc文件系统提供有关系统中进程的信息
ls /proc/
# 查看进程资源
ls /proc/{pid} 
当我ctrl+c终止掉这个程序的时候,僵尸进程也就不存在了

让我们来使用pcntl_wait()回收
$pid = pcntl_fork(); // 开启子进程
if ($pid == 0){
    echo "我是子进程,我的标识是:".posix_getpid().",(我接到了爸爸的电话,让我回家去),好,出发"."\n";
    echo "我距离我爸是15步"."\n";
    // 子进程运行任务
    $i = 0;
    while ($i<=14){
        echo "爸爸,我正在回家去,我走了".($i+1).'步'."\n";
        $i++;
        sleep(2);
    }
}else{
    echo "我是父进程,我的标识是:".posix_getpid().',过年了,我打电话让我儿子回家了'."\n";
    $exitPid = pcntl_wait($status);   // 阻塞状态就是一直等待子进程并回收
    echo "我儿子回来了,我的儿子是:".$exitPid."\n";
}
当儿子回家的时候我们通过另外一个连接查看儿子回家的状态

我们这次子进程不用sleep()函数再看一下效果
$pid = pcntl_fork(); // 开启子进程
if ($pid == 0){
    echo "我是子进程,我的标识是:".posix_getpid().",(我接到了爸爸的电话,让我回家去),好,出发"."\n";
    // 子进程运行任务
    while (1){
        ;
    }
}else{
    echo "我是父进程,我的标识是:".posix_getpid().',过年了,我打电话让我儿子回家了'."\n";
    $exitPid = pcntl_wait($status);   // 阻塞状态就是一直等待子进程并回收
    echo "我儿子回来了,我的儿子是:".$exitPid."\n";
}
我们再来看一下pcntl_wait()参数


pcntl_wait() 将会存储状态信息到 status 参数上,这个通过 status 参数返回的状态信息可以用以下函数 pcntl_wifexited(), pcntl_wifstopped(), pcntl_wifsignaled(), pcntl_wexitstatus(), pcntl_wtermsig() 以及 pcntl_wstopsig() 获取其具体的值。
pcntl_wait() 返回退出的子进程进程号,发生错误时返回 -1,如果提供了 WNOHANG 作为 option(wait3可用的系统)并且没有可用子进程时返回 0。
根据官网的例子说明,接下来我们分别测试:
$pid = pcntl_fork(); // 开启子进程
if ($pid == 0){
    echo "子进程标识是:".posix_getpid()."\n";
    // while (1){
    //     ;
    // }
}else{
    echo "父进程标识是:".posix_getpid()."\n";
    // 不阻塞状态 $exitPid 是返回 0  阻塞状态会等待子进程回收并返回子进程pid
    // 参数二就是不阻塞状态
    $exitPid = pcntl_wait($status,WNOHANG);   // 非阻塞
    // $exitPid = pcntl_wait($status);                // 阻塞
    // $exitPid 有三种状态 成功回收子进程 > 0  失败就是 -1  没有可用子进程返回 0
    // 我们这样就可以增加判断
    if ($exitPid == 0){
        echo "没有可回收进程"."\r\n";
    }elseif($exitPid > 0){
        // 回收子进程成功
        echo "回收子进程成功"."\n";
    }
}
进程退出以及退出状态演示:
// 北风之神yyds
// 1) return exit 函数 正常终止退出
// 2) 中断信号 异常终止退出
// 进程不管是何种方式退出,都会有一部分数据驻留在内存中,比如说终止状态,所以父进程必须使用pcntl_wait函数来回收终止进程所占用的系统资源
// 僵尸进程:就是子进程已经退出,但父进程还没有回收[pcntl_wait] Z
$pid = pcntl_fork(); // 开启子进程
if ($pid == 0){
    echo "子进程标识是:".posix_getpid()."\n";
    // die(5);
    // exit(7);
    // return 1;
    while (1){
        ;
    }
}else{
    echo "父进程标识是:".posix_getpid()."\n";
    // 不阻塞状态 $exitPid 是返回 0  阻塞状态会等待子进程回收并返回子进程pid
    // 参数二就是不阻塞状态
    // $exitPid = pcntl_wait($status,WNOHANG);   // 非阻塞
    $exitPid = pcntl_wait($status);                // 阻塞
    // $exitPid 有三种状态 成功回收子进程 > 0  失败就是 -1  没有可用子进程返回 0
    // 我们这样就可以增加判断
    if ($exitPid == 0){
        echo "没有可回收进程"."\r\n";
    }elseif($exitPid > 0){
        // 回收子进程成功了
        echo "回收子进程成功"."\n";
        // 接下来我们来判断退出状态
        if (pcntl_wifexited($status)){
            // pcntl_wifexited 检查子进程状态代码是否代表正常退出。正常退出时返回 true ,其他情况返回 false。
            // pcntl_wexitstatus  返回一个中断的子进程的返回代码
            echo "子进程是正常退出!"."代码是".pcntl_wexitstatus($status)."\n";
        }elseif (pcntl_wifstopped($status)){
            // 这个不知道怎么演示出来 *****  我测试 SIGSTOP SIGTSTP 这两个让进程停止的方法 不会进入到这里
            // pcntl_wifstopped  检查子进程当前是否已经停止 进程当前是停止的返回 true ,其他情况返回 false 。
            // pcntl_wstopsig 返回导致子进程停止的信号 返回信号编号。
            echo "子进程停止退出!停止信号是:".pcntl_wstopsig($status)."\n";
        }elseif (pcntl_wifsignaled($status)){
            // pcntl_wifsignaled 检查子进程状态码是否代表由于某个信号而中断 子进程是由于某个未捕获的信号退出的返回 true ,其他情况返回 false 。
            // pcntl_wtermsig  返回导致子进程中断的信号  返回整型的信号编号。
            echo "子进程非正常退出!中断信号是:".pcntl_wtermsig($status)."\n";
        }
    }
}
暂时不知道怎么测试出进程停止退出的状态
我测试信号 SIGSTOP SIGTSTP 这两个让进程停止的方法,也不行
所以跳过........
关于信号
# 查看所有信号
kill -l可以参考别的大佬的文章
Linux 信号的详细介绍和举例说明_易点点心动的博客-CSDN博客



















