僵尸进程:子进程终止了,但是父进程没有回收子进程的资源PCB。使其成为僵尸进程
孤儿进程:父进程先与子进程结束了,使得子进程失去了父进程,这个时候子进程会被1号进程init进程领养,成为孤儿进程
为了防止上面两种情况,我们应当在父进程结束之前一定要回收子进程的所有资源
所以出现了wait和waitpid
#include#include 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#include 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时可能会出现一下的情况:
- 阻塞(如果它的所有子进程都还在运行)。
- 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
- 出错立即返回(如果它没有任何子进程)。
上图看到了,wai成功的返回值
*******************************************************************************************
对于wait和waitpid两个函数,有所不同的是:
- 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
- wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。
所以,调用wait和waitpid不仅可以获得子进程的终止信息,还可以使父进程阻塞等待子进程终止,起到进程间同步的作用。如果参数status不是空指针,则子进程的终止信息通过这个参数传出,如果只是为了同步而不关心子进程的终止信息,可以将status参数指定为NULL。
1 #include2 #include 3 #include 4 #include 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返回-119 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 479736 I am child 479837 wait for child 479838 I am parent 479739 wait for child -140 I am parent 479741 wait for child -142 I am parent 4797
孤儿进程
#include#include #include 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 1I am 4813, my parent is 1I am 4813, my parent is 1I am 4813, my parent is 1I am 4813, my parent is 1I am 4813, my parent is 1
waitpad
1 #include2 #include 3 #include 4 #include 5 #include 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 child19 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 else29 { //in parent30 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 }