回收子进程

大纲

  • 孤儿进程
  • 僵尸进程
  • wait函数
  • waitpid函数

在Linux中正常情况下,子进程是通过父进程创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,也就是说,父进程永远无法预知到子进程会在什么时候结束。当一个进程完成工作终止后,它的父进程需要调用wait()waitpid()系统调用来获取子进程的终止状态。

子进程有两种比较重要的状态,分别是孤儿进程和僵尸进程。

孤儿进程

孤儿进程是指父进程先于子进程结束,通俗来讲也就是爹比儿子死得早,此时子进程成为孤儿进程,此时父进程会成为init进程(进程号pid为1的进程),init进程可视为进程孤儿院用于领养孤儿进程。

$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.4 225808  9512 ?        Ss   00:43   0:02 /sbin/init splash
$ ps aux | grep init
root         1  0.1  0.4 225808  9512 ?        Ss   00:43   0:02 /sbin/init splash
jc        3613  0.0  0.0  21536  1000 pts/0    S+   01:18   0:00 grep --color=auto init

当一个父进程退出而它的子进程还在运行,此时这些子进程将会成为孤儿进程,孤儿进程将被init进程收养,并由init进程对它们完成状态收集工作。

危害

孤儿进程是没有父进程的子进程,因此孤儿进程的管理重任会落到init进程身上,init进程好像一个民政局或孤儿所,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程时内核就会把孤儿进程的父进程设置为init进程,init进程会循环地wait已经退出的子进程。这样,当一个孤儿进程的生命周期结束时,init进程会代表党和政府处理善后工作,因此孤儿进程不会有什么危害。

实例1

$ vim orphan.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    pid_t pid;
    pid = fork();//创建子进程
        //如果当前进程是init进程则报错退出
    if(pid == 1)
    {
        perror("init process");
        exit(1);
    }
    else if(pid > 0)
    {
        sleep(1);
        printf("parent pid = %d, parent parent pid is %d\n", getpid(), getppid());
    }
    else if(pid == 0)
    {
        printf("child pid is %d, parent pid is %d\n", getpid(), getppid());
        sleep(3);
        printf("child pid is %d, parent pid is %d\n", getpid(), getppid());

    }

    return 0;
}
$ gcc orphan.c -o orphan -Wall -g
$ ./orphan
child pid is 3652, parent pid is 3651
parent pid = 3651, parent parent pid is 3633
root@junchow:/home/jc/projects/c# pachild pid is 3652, parent pid is 2700
$ ps aux | grep 2700
jc        2700  0.0  0.3  77120  8292 ?        Ss   00:57   0:00 /lib/systemd/systemd --user
root      3672  0.0  0.0  21536  1052 pts/0    S+   01:21   0:00 grep --color=auto 2700

实例2

$ vim orphan.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    pid = fork();
    if(pid == 0)
    {
        while(1)
        {
            printf("child: parent pid=%d\n", getppid());
            sleep(1);
        }
    }
    else if(pid > 0)
    {
        printf("parent: pid=%d\n", getpid());
        sleep(10);
        printf("parent: going to die\n");
    }
    else
    {
        perror("fork");
        return 1;
    }
    return 0;
}
$ gcc orphan.c -o orphan -Wall -g
$ ./orphan

僵尸进程

僵尸进程是如何产生的呢?

当运行一个程序时会产生一个父进程以及多个子进程,所有子进程都会消耗内核分配的内存和CPU资源。子进程完成执行后会发送一个exit信号然后死掉。这个exit型号需要被父进程读取。父进程需要随后调用wait命令来读取进程的退出状态,并将子进程从进程表中移除。

在UNIX系统中一旦进程结束,如果父进程没有使用waitwaitpid系统调用等待子进程结束,又没有显式的忽略SIGCHILD信号,那么它将变成一个僵尸进程并一直存在。僵尸进程是一个早已死亡的进程,但在进程表中任占据着一个位置。

如果子进程的父进程已先行结束,那么子进程就不会变成僵尸进程,因为每个进程结束的时候,系统会扫描当前操作系统中所有运行的进程,检查有没有哪个进程是刚刚结束的进程的子进程。若有则由init进程来接管并作为它的父进程,从而保证每个进程都会有一个父进程。init进程会自动wait其子进程,因此被init接管的进程都不会变成僵尸进程。

4933701-8cf872f8c5153645.png
僵尸进程

僵尸进程是指进程终止,父进程尚未回收,子进程残留资源PCB存放于内核kernel中变为僵尸zombie进程。

父进程有义务将子进程回收,如果子进程死亡后父进程不帮子进程收尸,此时子进程就会变成僵尸。

如何查找僵尸进程呢?

$ ps -ef | grep defunct
$ ps aux | grep Z

僵尸进程存在会有什么样的危害呢?

正常进程死亡以后,0到4G的进程地址空间会主动释放,但是PCB依然会残留在内核中,其目的是为了让父进程为它报仇。如果子进程死亡后没有任何痕迹留下的话,父进程将无法知道子进程是由于什么原因造成的死亡,是自杀还是他杀呢?子进程死亡后会在内核中残留PCB,父进程通过PCB可以获取子进程的死亡状态。

任何一个子进程(init进程除外)在exit退出后并非立即消失,而会留下一个称为僵尸进程的数据结构,以等待父进程处理。

UNIX提供了一种机制让父进程获取子进程结束时的状态信息,这种机制就是在每个子进程退出的时候,内核会释放该进程所有的资源,包括打开的文件、占用的内存等。但内核仍然会保留一定的信息,如进程号、退出状态、运行时间等信息。直到父进程通过waitwaitpid系统调用来获取时才完全释放。

这样就导致了一个问题,如果进程不调用 waitwaitpid,那么保留在内核中的残留信息将不会主动被释放,进程号会一直被占用。由于系统所能使用的进程号是有限的,如果大量产生僵尸进程的话,系统将没有可用的进程号,从而导致系统不能产生新的进程。

此时如果使用ps命令查看会发现子进程的状态为Z,即僵尸进程。

严格来说,僵尸进程并非罪魁祸首,问题的根源在于产生大量僵尸进程的那个父进程。可以使用kill命令发送信号SIGTERMSIGKILL信号枪毙元凶进程。枪毙完成后,僵尸进程将会变成了孤儿进程,这些孤儿进程会被ini进程接管。init进程会wait`这些孤儿进程,释放他们占用的系统资源。

需要注意的是僵尸进程不能使用kill命令清除,因为kill命令只是用来终止进程,而僵尸进程已经是终止的。

如何避免僵尸进程呢?

僵尸进程的产生是因为父进程没有wait子进程,当系统中出现了僵尸进程,是无法通过kill命令来清除的,但可以杀死僵尸进程的父进程,让其变成孤儿进程,系统会统一管理和清理孤儿进程。

有什么办法可以清除掉僵尸进程呢?

正常情况下可以使用SIGKILL信号来杀死进程,由于僵尸进程已经早死了,所以不能使用kill命令杀死已经死掉的进程。

$ kill -s SIGCHILD pid

可使用waitwaitpid回收僵尸进程

实例

$ vim zoombie.c
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    
    //创建子进程
    pid = fork();
    printf("pid=%d\n", pid);

    //若子进程为调度进程
    if(pid == 0)
    {
        printf("child: parent=%d\n", getppid());
        printf("child: going to sleep\n");
        sleep(10);
        printf("child: child die\n");
    }
    else if(pid > 0)
    {
        while(1)
        {
            printf("parent: pid=%d child=%d\n", getpid(), pid);
            sleep(1);
        }
    }
    else
    {
        perror("fork error");
        return 1;
    }

    return 0;
}

$ gcc zoombie.c -o zoombie -Wall -g
$ ./zoombie

运行结果

pid=3654
parent: pid=3653 child=3654
pid=0
child: parent=3653
child: going to sleep
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
child: child die
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
...

注意若运行出现implicit declaration of function ‘fork’ [-Wimplicit-function-declaration]则需注意是否引入了unistd.h头文件。

wait函数

一个进程在终止时会关闭所文件描述符,释放用户空间并分配内存,只是子进程的PCB还保留着,内核在其中保持努着一些信息。如果是正常终止或保存退出状态。如果是异常终止则保存着导致该进程的信号,这个进程可使用Shell中特殊。

父进程调用wait系统调用可以回收子进程的终止信息,然后彻底清除掉这个进程,一个进程的状态可以在Shell中的特殊变量$?来查看。

wait函数的三个功能分别是

  1. 阻塞等待子进程退出
  2. 回收子进程残留的信息
  3. 获取子进程结束状态与退出原因
pid_t wait(int *status)

wait函数成功则清理掉子进程ID,若失败则返回-1表示没有子进程。

当进程终止时操作系统的隐式回收机制

  1. 关闭所有文件描述符
  2. 释放用户空间分配的内存

进程终止时内核的PCB仍旧存在,其中保存该进程的退出状态,简单来说,正常终止时返回退出值,异常终止时返回终止信号。

实例:父进程回收子进程

$ vim wait.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;//创建子进程的进程ID
    pid_t wpid;//回收子进程的返回值
    
    //创建子进程
    pid = fork();
    printf("pid=%d\n", pid);

    //若子进程为调度进程
    if(pid == 0)
    {
        //子进程休眠10秒后死亡
        printf("child: parent=%d\n", getppid());
        printf("child: going to sleep\n");
        sleep(10);
        printf("child: child die\n");
    }
    else if(pid > 0)
    {
        //回收子进程若失败则提示错误
        wpid = wait(NULL);
        if(wpid == -1)
        {
            perror("wait error");
            exit(1 );
        }

        while(1)
        {
            printf("parent: pid=%d child=%d\n", getpid(), pid);
            sleep(1);
        }
    }
    else
    {
        perror("fork error");
        return 1;
    }

    return 0;
}
$ gcc wait.c -o wait -Wall -g
$ ./wait
pid=3757
pid=0
child: parent=3756
child: going to sleep
child: child die
parent: pid=3756 child=3757
parent: pid=3756 child=3757
parent: pid=3756 child=3757
...

此时若查看进程状态会发现并未出现僵尸进程

$ ps aux|grep wait
jc        3756  0.0  0.0   4508   740 pts/0    S+   22:42   0:00 ./wait
jc        3767  0.0  0.0  21536  1060 pts/1    S+   22:42   0:00 grep --color=auto wait

子进程死亡状态

当进程终止时刻使用wait函数传出参数status来保存进程的推出状态,借助宏函数来进一步判断进程终止的具体原因。宏函数可分为三组:

  1. 正常退出
  • WIFEXITED(status) :全称wait if exited表示判断是否是正常退出的
    若非0则表示进程正常退出
  • WEXITSTATUS(status) :全称wait exit status表示退出状态
    WIFEXITED(status)为真则使用宏WEXITSTATUS(status)以获取进程退出的状态,即exit的参数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;//创建子进程的进程ID
    pid_t wpid;//回收子进程的返回值
    int status;//回收子进程返回的状态
    //创建子进程
    pid = fork();
    printf("pid=%d\n", pid);

    //若子进程为调度进程
    if(pid == 0)
    {
        //子进程休眠3秒后死亡
        printf("child-%d: parent=%d\n", pid, getppid());
        printf("child-%d: going to sleep 3 seconds\n", pid);
        sleep(3);
        printf("child-%d: child die\n", pid);
        exit(76);//退出值不超过128
    }
    else if(pid > 0)
    {
        //回收子进程
        wpid = wait(&status);
        //回收子进程若失败则提示错误
        if(wpid == -1)
        {
            perror("wait error");
            exit(1);
        }
        //若子进程回收成功
        printf("child-%d: wait if existed with %d\n", pid, WIFEXITED(status));
        if(WIFEXITED(status) != 0)
        {
            //获取子进程成功退出的状态值
            printf("child-%d: wait exit status with %d\n", pid, WEXITSTATUS(status));
        }


        while(1)
        {
            printf("parent-%d: child=%d\n", getpid(), pid);
            sleep(1);
        }
    }
    else
    {
        perror("fork error");
        return 1;
    }

    return 0;
}

编译运行程序并查看状态

$ gcc wait.c -o wait -Wall -g
$ ./wait
pid=3934
pid=0
child-0: parent=3933
child-0: going to sleep 3 seconds
child-0: child die
child-3934: wait if existed with 1
child-3934: wait exit status with 76
parent-3933: child=3934
parent-3933: child=3934
parent-3933: child=3934
...

注意:此处使用exit(76);也可以使用return 76;进行替换。

  1. 异常退出
  • WIFSIGNALED(status) :全称wait if signaled
    若非0则表示进程异常终止
  • WTERMSIG(status):全称wait term signal
    WIFSIGNALED(status)为真则使用宏WTERMSIG(status)以获得使进程终止的那个信号的编号。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;//创建子进程的进程ID
    pid_t wpid;//回收子进程的返回值
    int status;//回收子进程返回的状态
    //创建子进程
    pid = fork();
    printf("pid=%d\n", pid);

    //若子进程为调度进程
    if(pid == 0)
    {
        //子进程休眠后死亡
        printf("child-%d: parent=%d\n", pid, getppid());
        printf("child-%d: going to sleep 60 seconds\n", pid);
        sleep(60);
        printf("child-%d: child die\n", pid);
        exit(76);//退出值不超过128
    }
    else if(pid > 0)
    {
        //回收子进程
        wpid = wait(&status);
        //回收子进程若失败则提示错误
        if(wpid == -1)
        {
            perror("wait error");
            exit(1 );
        }
        //若子进程正常退出
        printf("child-%d: wait if existed with %d\n", pid, WIFEXITED(status));
        if(WIFEXITED(status) != 0)
        {
            //获取子进程成功退出的状态值
            printf("child-%d: wait exit status with %d\n", pid, WEXITSTATUS(status));
        }
        //若子进程异常退出
        printf("child-%d: wait if signaled with %d\n", pid, WIFSIGNALED(status));
        if(WIFSIGNALED(status) != 0)
        {
            printf("child-%d: wait term sig with %d\n", pid, WTERMSIG(status));
        }


        while(1)
        {
            printf("parent-%d: child=%d\n", getpid(), pid);
            sleep(1);
        }
    }
    else
    {
        perror("fork error");
        return 1;
    }

    return 0;
}

编译并运行程序

$ gcc wait.c -o wait -Wall -g
$ ./wait

此时让子进程休眠60秒,为什么呢,在60秒内新开命令行窗口并杀死子进程和父进程查看输出状态。

$ ps aux|grep wait
root      1139  0.0  0.7 194320 17560 ?        Ssl  22:03   0:00 /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal
jc        4030  0.0  0.0   4508   856 pts/0    S+   23:44   0:00 ./wait
jc        4031  0.0  0.0   4508    72 pts/0    S+   23:44   0:00 ./wait
jc        4034  0.0  0.0  21536  1148 pts/1    S+   23:44   0:00 grep --color=auto wait

此时会发现有两个进程正在运行,4030对应的应该是父进程,4031对应的应该是子进程,接下来分别杀死进程。

$ kill -9 4031
$ kill -9 4030

最后查看日志输出信息

pid=4031
pid=0
child-0: parent=4030
child-0: going to sleep 60 seconds
child-4031: wait if existed with 0
child-4031: wait if signaled with 1
child-4031: wait term sig with 9
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
已杀死

此处需注意child-4031: wait term sig with 9中的9是由于使用kill -9 4031,也就是向进程发送的9号信号。表示什么意思呢?

$ kill -L
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

根据命令可以知道9号信号表示SIGKILL

  1. 暂停状态
  • WIFSTOPPED(status):全称wait if stopped进程处于暂停状态
    若非0则表示进程处于暂停状态
  • WSTOPSIG(status)
    WIFSTOPPED(status)为真则使用宏WSTOPSIG(status)以获取使进程暂停的那个信号的编号。
  • WIFCONTINUED(status)
    WIFCONTINUED(status)为真则表示进程暂停后已经继续运行

子进程死亡一般会存在两种情况,一种是正常死亡寿终正寝,此时程序将返回0。另一种情况是异常退出,在Linux中所有的异常退出都是由于信号导致的,由于子进程收到了某个特殊信号它才异常退出。所以异常终止时父进程需要回收子进程异常终止的信号。

waitpid函数

waitpid函数作用于wait函数相同,但可以指定pid进程清理且不阻塞。

pid_t waitpid(pid_t pid, int *status, int options)

waitpid若成功则返回清理掉的进程ID,若失败则返回-1表示无子进程。

wait函数与waitpid的区别在于一次wait调用只能回收一个子进程,而waitpid可以指定进程ID进行回收更加灵活。

waitpid参数pid返回情况

  • pid小于-1表示回收指定进程组中的任意子进程
  • pid等于0表示回收和当前调用waitpid一个组的所有子进程
  • pid等于-1表示回收任意子进程,作用相当于wait函数。
  • pid大于0表示回收指定进程ID的子进程

waitpid参数options可以设置是否阻塞,设置非阻塞可使用WNOHANG宏,设置非阻塞后需要使用轮询的方式定时查看子进程是否回收成功。

$ vim waipid.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int n = 5;//默认创建5个子进程
    int i;//循环变量
    pid_t pid;//子进程ID
    pid_t tmp;

    //若命令行参数个数是2个
    if(argc == 2)
    {
        n = atoi(argv[1]);
    }
    //循环创建子进程
    for(i=0; i<n; i++)
    {
        pid = fork();
        //若进程为系统进程
        if(pid == 0)
        {
            break;
        }
        else if(i ==3)
        {
            tmp = pid;
        }
    }
    printf("tmp=%d\n", tmp);
    if(n == i)
    {
        sleep(n);//多少个循环则休眠多少秒
        printf("parent: pid=%d\n", getpid());//打印父进程
        //子进程死亡后回收
        wait(NULL);//随机回收子进程且只回收一个
        while(1);//死循环
    }
    else
    {
        sleep(i);//休眠
        printf("child%d: pid=%d\n", i+1, getpid());//打印子进程
        //while(1);//死循环
    }

    return 0;
}
$ gcc waitpid.c -o waitpid -Wall -g
$ ./waitpid
tmp=5043
tmp=5043
tmp=0
tmp=0
tmp=0
tmp=0
child1: pid=5040
child2: pid=5041
child3: pid=5042
child4: pid=5043
child5: pid=5044
parent: pid=5039
$ ps aux|grep waitpid
jc        5039 81.6  0.0   4508   756 pts/0    R+   03:52   0:25 ./waitpid
jc        5041  0.0  0.0      0     0 pts/0    Z+   03:52   0:00 [waitpid] <defunct>
jc        5042  0.0  0.0      0     0 pts/0    Z+   03:52   0:00 [waitpid] <defunct>
jc        5043  0.0  0.0      0     0 pts/0    Z+   03:52   0:00 [waitpid] <defunct>
jc        5044  0.0  0.0      0     0 pts/0    Z+   03:52   0:00 [waitpid] <defunct>

可以查看到当前存在4个僵尸进程Z+,使用wait回收时每次只回收了一个进程,如果需要同时回收5个进程,应该怎么办呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int n = 5;//默认创建5个子进程
    int i;//循环变量
    pid_t pid;//子进程ID
    pid_t tmp;

    //若命令行参数个数是2个
    if(argc == 2)
    {
        n = atoi(argv[1]);
    }
    //循环创建子进程
    for(i=0; i<n; i++)
    {
        pid = fork();
        //若进程为系统进程
        if(pid == 0)
        {
            break;
        }
        else if(i ==3)
        {
            tmp = pid;
        }
    }
    printf("tmp=%d\n", tmp);
    if(n == i)
    {
        sleep(n);//多少个循环则休眠多少秒
        printf("parent: pid=%d\n", getpid());//打印父进程
        //子进程死亡后回收
        while(wait(NULL));//循环回收子进程,每次只能回收一个。
        while(1);//死循环
    }
    else
    {
        sleep(i);//休眠
        printf("child%d: pid=%d\n", i+1, getpid());//打印子进程
        //while(1);//死循环
    }

    return 0;
}

此处会发现如果需要回收多个进程,可使用while循环的方式。

如果现在需要指定某个进程进行回收呢?应该怎么办?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int n = 5;//默认创建5个子进程
    int i;//循环变量
    pid_t pid;//子进程ID
    pid_t tmp;

    //若命令行参数个数是2个
    if(argc == 2)
    {
        n = atoi(argv[1]);
    }
    //循环创建子进程
    for(i=0; i<n; i++)
    {
        pid = fork();
        //若进程为系统进程
        if(pid == 0)
        {
            break;
        }
        else if(i ==3)
        {
            tmp = pid;//获取第三个子进程的pid,为了指定进程进行回收使用。
        }
    }
    printf("tmp=%d\n", tmp);
    if(n == i)
    {
        sleep(n);//多少个循环则休眠多少秒
        printf("parent: pid=%d\n", getpid());//打印父进程
        //子进程死亡后回收
        waitpid(tmp, NULL, 0);//指定回收某个子进程
        while(1);//死循环
    }
    else
    {
        sleep(i);//休眠
        printf("child%d: pid=%d\n", i+1, getpid());//打印子进程
        //while(1);//死循环
    }

    return 0;
}
$ gcc waitpid.c -o waitpid -Wall -g
$ ./waitpid
tmp=5097
tmp=5097
tmp=0
tmp=0
tmp=0
tmp=0
child1: pid=5094
child2: pid=5095
child3: pid=5096
child4: pid=5097
child5: pid=5098
parent: pid=5093
$ ps aux|grep waitpid
jc        5093 54.5  0.0   4508   740 pts/0    R+   04:05   0:06 ./waitpid
jc        5094  0.0  0.0      0     0 pts/0    Z+   04:05   0:00 [waitpid] <defunct>
jc        5095  0.0  0.0      0     0 pts/0    Z+   04:05   0:00 [waitpid] <defunct>
jc        5096  0.0  0.0      0     0 pts/0    Z+   04:05   0:00 [waitpid] <defunct>
jc        5098  0.0  0.0      0     0 pts/0    Z+   04:05   0:00 [waitpid] <defunct>
jc        5100  0.0  0.0  21536  1028 pts/1    S+   04:05   0:00 grep --color=auto waitpid

此处会发现pid=5087的子进程被回收了

未完待续...

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值