基本概念
查看进程的命令
ps -ajx
孤儿进程
父进程领先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程,mac的init进程的pid=1
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
pid_t pid;
pid = fork();
if(pid==0){ //让子进程一直活着
while(1){
printf("I am child process, my parent pid = %d\n",getppid());
sleep(2);
}
}else if(pid>0){ //让父进程活一会儿
printf("I am parent process, my pid = %d\n",getpid());
sleep(9);
}else if(pid<0){
perror("create child process error");
exit(1);
}
}
僵尸进程
子进程先于父进程死亡。进程终止,父进程尚未回收该进程,子进程残留资源(PCB)存放于内核中,变成僵尸进程。就是没人收尸,叫僵尸。所有进程肯定都会经历,不可能你一结束,你的父进程就能回收你。
- 僵尸进程不能用kill,kill是用来终止进程的,但是人家本来就已经终止了,只是资源没有回收,你kill就没用,要用wait主动回收
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
pid_t pid;
pid = fork();
if(pid==0){ //让子进程死
printf("I am child process, my parent pid = %d\n",getppid());
sleep(9);
printf("child process die\n");
}else if(pid>0){ //让父进程活着
while(1){
printf("I am parent process, my pid = %d, my child pid = %d\n",getpid(),pid);
sleep(2);
}
}else if(pid<0){
perror("create child process error");
exit(1);
}
}
查询死亡后的子进程号,显示defunct失去功能,即僵尸进程
wait函数
pid_t wait(int* status);
返回值:
- 成功:回收的子进程pid
- 失败:-1,设置errono
传出参数:
- status:关于子进程终止的一些情况
函数作用:
- 阻塞等待子进程退出
- 清理子进程残留在内核的pcb
- 通过传出参数,得到子进程结束状态
status作用:
- 获取子进程正常终止值:
- WIFEXITED(status) -> 为真 -> WEXITSTATUS(status) -> 得到子进程退出值
- 获取导致子进程异常终止的信号:
- WIFSIGNALED(status) -> 为真 -> WTERMSIG(staus) -> 得到导致子进程一场终止的信号值
如果你不需要使用传出参数:
pid_t pid=wait(NULL);
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
pid_t pid;
pid = fork();
int status;
if(pid==0){
printf("I am child process,my pid=%d, my parent pid = %d\n",getpid(),getppid());
sleep(9);
printf("child process die\n");
return 74; //status可以监测到返回值
}else if(pid>0){
pid_t wpid = wait(&status); //会阻塞等待子进程死亡
if(wpid==-1){
perror("wait error");
exit(1);
}
//wpid返回的是被回收的子进程的pid,status存放关于子进程终止的信息
if(WIFEXITED(status)){ //为真,表示子进程正常终止,exit和return都算
printf("child exit with %d\n",WEXITSTATUS(status)); //返回正常终止时的返回值
}
if(WIFSIGNALED(status)){ //为真,表示子进程被信号终止,kill命令之类的
printf("child kill with signal %d\n",WTERMSIG(status)); //返回终止进程的信号
}
}else if(pid<0){
perror("create child process error");
exit(1);
}
}
waitpid
pid_t waitpid(pid_t pid, int *status, int option)
返回值:
- >0:成功回收的子进程pid
- 0:函数调用时,子进程还没结束,而且option指定了WNOHANG,即不阻塞状态下没回收到东西
- -1:失败,设置errno
参数:
- pid:指定回收的子进程pid
- >0:成功回收的子进程pid
- -1:任意子进程
- 0:同组子进程
- status:关于子进程终止的一些情况
- option:可以设置非阻塞,WNOHANG,wait not hang,不挂起
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
int main(int argc, char* argv[]){
int i;
pid_t pid,wpid,pid_2;
for(int i=0;i<5;i++){
pid=fork();
if(pid==-1){
perror("fork error");
exit(1);
}
if(pid==0){ //如果当前是子进程就退出
printf("I am the %dth child, my pid is %d\n",i,getpid());
sleep(i);
exit(1);
}
if(i==2){ //记录第三个子进程的pid
pid_2=pid;
}
}
sleep(5);
wpid = waitpid(pid_2,NULL,WNOHANG); //回收第二个子进程,不阻塞
//wpid = waitpid(-1,NULL,WNOHANG); //回收任意子进程,不阻塞
//wpid = waitpid(-1,NULL,0); //回收任意子进程,因为WNOHANG是宏,你随便填一个其他的就阻塞了
printf("I am parent, wait a child finish: %d \n",wpid);
}
wait和waitpid注意
一次调用就只回收一个子进程,只有waitpid可以指定回收
wait(&status) = waitpid(-1,&status,0)
回收多个子进程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
int main(int argc, char* argv[]){
int i;
pid_t pid,wpid,pid_2;
for(int i=0;i<5;i++){
pid=fork();
if(pid==-1){
perror("fork error");
exit(1);
}
if(pid==0){
printf("I am the %dth child, my pid is %d\n",i,getpid());
exit(1);
}
wpid = waitpid(pid,NULL,0); //没回收子进程,就阻塞,不会进入下一次创建
printf("I am parent, wait a child finish: %d \n",wpid);
}
}