首页 > 代码库 > 进程——wait与waitpid、僵尸进程与孤儿进程

进程——wait与waitpid、僵尸进程与孤儿进程

僵尸进程:子进程终止了,但是父进程没有回收子进程的资源PCB。使其成为僵尸进程

孤儿进程:父进程先与子进程结束了,使得子进程失去了父进程,这个时候子进程会被1号进程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进程清除,那么就产生了僵尸进程,正常情况下,僵尸进程都被父进程清理了

下面写一个不正常了程序,使得父进程不结束

#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;
}

上面中,父进程一直处于while(1)循环,使得父进程不退出,下图查看ps aux可以发现父进程状态为R,子进程状态为Z(僵尸态Zombie)

 

 

技术分享

 

 

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

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

 技术分享

 

技术分享

 上图看到了,wai成功的返回值

*******************************************************************************************

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

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

 

 

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

 

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <sys/types.h>
 5 
 6 int main(void)
 7 {
 8     pid_t pid,pid_c;
 9 
10     int n = 10;
11     pid = fork();
12 
13     if(pid > 0 )
14     {/* in parent */
15         while(1)
16         {
17             printf("I am parent %d\n",getpid());
18             //wait是一个阻塞函数,等待回收子进程资源,如果没有子进程,wait返回-1
19             pid_c = wait(NULL);
20             printf("wait for child %d\n",pid_c);
21             sleep(1);
22         }
23     }
24     else if(pid == 0)
25     {/* in child */
26         printf("I am child %d\n",getpid());
27         sleep(10);
28     }
29 
30     return 0;
31 }
32 
33 
34 运行结果:
35 I am parent 4797
36 I am child 4798
37 wait for child 4798
38 I am parent 4797
39 wait for child -1
40 I am parent 4797
41 wait for child -1
42 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

waitpad

 1 #include <sys/types.h>
 2 #include <sys/wait.h>
 3 #include <unistd.h>
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 
 7 int main(void)
 8 {
 9     pid_t pid;
10     pid = fork();
11     if (pid < 0)
12     {
13         perror("fork failed");
14         exit(1);
15     }
16 
17     if (pid == 0)
18     {//in child
19         int i;
20         for (i = 3; i > 0; i--) {
21             printf("This is the child %d\n",getpid());
22             sleep(1);
23         }
24         exit(3);//返回3,运行时可以看到
25         //子进程睡眠3秒之后就退出了
26     }
27 
28     else
29     {//in parent
30         int stat_val;
31         waitpid(pid, &stat_val, 0);//以阻塞方式等待回收子进程,第三个参数0,表示阻塞方式
32         if (WIFEXITED(stat_val))//正常退出
33             printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
34         else if (WIFSIGNALED(stat_val))//查看被什么信号关闭
35     printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
36     }
37     return 0;
38 }

 

进程——wait与waitpid、僵尸进程与孤儿进程