孤儿进程:
父进程先死了,子进程被init进程领养,即子进程的父进程pid变成了1
这个子进程就叫孤儿进程
僵尸进程:
子进程死了,但是父进程没有回收子进程的资源(子进程的资源必须由父进程回收,操作系统不会自动回收)
任何进程都会存在僵尸进程阶段,只不过有些及时被回收了
因为僵尸进程是已经死掉的进程,因此用kill没办法回收僵尸进程
子进程回收
一个进程终止时关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留的,内核在其中保留一些信息:
如果是正常终止,则保留着退出状态
如果是异常终止,则保存导致该进程终止的信号是哪个
终止进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程
wait函数
作用:
1 阻塞等待,即在父进程中调用的wait会一直等待子进程死亡然后再进行回收
2 回收子进程资源
3 查看死亡原因
pid_t wait(int *status)
status: 是一个传出参数,即定义一个整型变量,然后把该整型变量传进wait函数中就行,然后操作系统会给该形参复制,从而获取进程的结束状态
返回值:如果成功,返回已经终止的子进程的pid,如果失败返回-1
int main(){ pid_t pid = fork(); if(pid == 0){ cout<<"孩子"<<endl; sleep(2); } else if(pid > 0){ cout<<"父进程"<<endl; int status; // 整型数,用来接收子进程终止状态 pid_t wpid = wait(&status); // 如果子进程不死,则父进程会停留再这一行语句阻塞等待,如果不看终止状态的话,直接传入NULL就行,不用特地传个整型数 if(WIFEXITED(status)){ cout<<"退出状态"<<WEXITEDSTATUS(status)<<endl; } if(WIFSIGNALED(status)){ cout<<"孩子被几号信号杀死?"<<WTERMSIG(status)<<endl; } cout<<"孩子die"<<endl; while(1){ sleep(1); } } return 0; }
子进程死亡原因可以把status传入几个宏来获取
正常死亡:WIFEXITED(status) == true, 代表正常死亡,WEXITSTATUS(status)得到退出状态(退出状态一般指的是子进程的返回值,比如return 101, 那就是101,exit(544),那就是544)
非正常死亡:WIFSIGNALED(status) == true, 代表非正常死亡,WTERMSIG得到是第几号信号将其杀死的
waitpid函数
pid_t waitpid(pie_t pid, int *status, int options)
返回值:
成功:返回清理掉的子进程ID
失败:-1(当当前进程没有子进程的时候,回收失败)
如果设置了WNOHANG,那么会返回0
参数pid:
>0 回收指定ID的子进程
-1 回收任意子进程
0 回收进程组ID与当前父进程的进程组ID相同的子进程,这说明了一个问题,虽然一般情况下子进程的进程组ID等于父进程的进程组ID,但是子进程的进程组ID也是可以不等于其父进程的进程组ID的,这种情况下就不能用pid=0来回收该进程了,所以还是用pid=-1比较好
-1 回收指定进程组内的任何子进程,这个进程组的组ID就是pid的绝对值,这意味着有多个子进程其组ID相同,但是与父进程的组ID不同,但血浓于水,父进程还是要回收他们
options:
可以为0或者WNOHANG
0: 跟wait一样,waitpid会发生阻塞等待
WNOHANG: 如果没有子进程退出,会立刻返回
回收多个子进程
wait和waitpid一次都是只能回收一个子进程,如果要回收多个子进程,可能就要用一个循环依次回收了
现在给出waitpid回收逻辑
if(i == 5){ // 父进程的逻辑 while(1){ pid_t wpid = waitpid(-1, NULL, WNOHANG); if(wpid == -1){ // 说明已经没有子进程 break; } else if(wpid > 0){ cout<<"回收子进程ID"<<wpid<<endl; } } }