僵尸进程:子进程终止了,但是父进程没有回收子进程的资源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时可能会出现一下的情况:
- 阻塞(如果它的所有子进程都还在运行)。
- 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
- 出错立即返回(如果它没有任何子进程)。
上图看到了,wai成功的返回值
*******************************************************************************************
对于wait和waitpid两个函数,有所不同的是:
- 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
- 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 }