(二十五)进程——wait与waitpid、僵尸进程与孤儿进程

  僵尸进程: 子进程退出,父进程没有回收子进程资源(PCB),则子进程变成僵尸进程。
  孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为1号进程init进程,称为init进程领养孤儿进程。
  为了防止以上情况的出现,就需要使用wait或waitpid在父进程退出前回收子进程的资源,而父进程应当在回收完所有子进程的资源后才能退出。

#include <sys/types.h>
#include <sys/wait.h>

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


status是一个传出参数。
waitpid的pid参数选择:
< -1 回收指定进程组内的任意子进程
= -1 回收任意子进程
= 0  回收和当前调用waitpid一个组的所有子进程
> 0  回收指定ID的子进程


  一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程
  如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了,为了观察到僵尸进程,我们自己写一个不正常的程序,父进程fork出子进程,子进程终止,而父进程既不终止也不调用wait清理子进程:

#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    pid_t pid=fork();

    if(pid<0) {
        perror("fork");
        exit(1);
    }

    if(pid>0) { /* parent */
        while(1);
    }

    /* child */
    return 0;
}

  对于wait或waitpid函数若调用成功则返回清理掉的子进程id若调用出错则返回-1。父进程调用wait或waitpid时可能会出现一下的情况:

  1. 阻塞(如果它的所有子进程都还在运行)。
  2. 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
  3. 出错立即返回(如果它没有任何子进程)。


  对于wait和waitpid两个函数,有所不同的是:

  1. 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
  2. wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。


  所以,调用wait和waitpid不仅可以获得子进程的终止信息,还可以使父进程阻塞等待子进程终止,起到进程间同步的作用。如果参数status不是空指针,则子进程的终止信息通过这个参数传出,如果只是为了同步而不关心子进程的终止信息,可以将status参数指定为NULL。
  



例子:

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

int main(void)
{
    pid_t pid,pid_c;

    int n = 10;
    pid = fork();

    if(pid > 0 )
    {/* in parent */
        while(1)
        {
            printf("I am parent %d\n",getpid());
            //wait是一个阻塞函数,等待回收子进程资源,如果没有子进程,wait返回-1
            pid_c = wait(NULL);
            printf("wait for child %d\n",pid_c);
            sleep(1);
        }
    }
    else if(pid == 0)
    {/* in child */
        printf("I am child %d\n",getpid());
        sleep(10);
    }

    return 0;
}


运行结果:
I am parent 4797
I am child 4798
wait for child 4798
I am parent 4797
wait for child -1
I am parent 4797
wait for child -1
I am parent 4797
·
·
·

孤儿进程的例子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    pid_t pid;
    int n=10;
    pid = fork();
    if(pid > 0)
    {//创建完之后父进程就退出了
        printf("I am parent\n");
        exit(0);
    }
    else if(pid == 0)
    {//此时父进程退出,子进程被init程序接管,该进程的父进程号变成1
        while(n--)
        {
            printf("I am %d, my parent is %d\n",getpid(),getppid());
            sleep(1);
        }
    }
    else
    {
        perror("fork");
        exit(-1);
    }
    return 0;
}


运行结果:
I am 4813, my parent is 1
I am 4813, my parent is 1
I am 4813, my parent is 1
I am 4813, my parent is 1
I am 4813, my parent is 1
I am 4813, my parent is 1

waitpid使用举例:

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

int main(void)
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork failed");
        exit(1);
    }

    if (pid == 0)
    {//in child
        int i;
        for (i = 3; i > 0; i--) {
            printf("This is the child %d\n",getpid());
            sleep(1);
        }
        exit(3);//返回3,运行时可以看到
        //子进程睡眠3秒之后就退出了
    }

    else
    {//in parent
        int stat_val;
        waitpid(pid, &stat_val, 0);//以阻塞方式等待回收子进程,第三个参数0,表示阻塞方式
        if (WIFEXITED(stat_val))//正常退出
            printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
        else if (WIFSIGNALED(stat_val))//查看被什么信号关闭
    printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
    }
    return 0;
}
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值