孤儿进程与僵尸进程
孤儿进程:父亲死了,子进程被init进程领养
僵尸进程:子进程死了,父进程没有回收子进程的资源( PCB),父进程要知道自己儿子是怎么死的
如何回收僵尸进程:杀死父亲, init领养,负责回收
僵尸进程对系统是有危害的,他会一直占用资源
孤儿进程代码
#include
#include
int main()
{
pid_t pid = fork();
if(pid == 0 ){
while(1){
printf("I am child,pid=%d,ppid=%d\n",getpid(),getppid());
sleep(1);
}
}else if(pid > 0){
printf("I am parent,pid=%d,ppid=%d\n",getpid(),getppid());
sleep(5); //父进程先结束,但是子进程没有结束, 此时父进程id会变成1
printf("I am parent,I will die!\n");
}
return 0;
}
僵尸进程代码
#include
#include
int main()
{
pid_t pid = fork();
if(pid == 0){
printf("I am child,pid =%d,ppid=%d\n",getpid(),getppid());
sleep(4);
printf("I am child,I will die!\n");
}
else if(pid > 0){
while(1){
printf("I am father,very happy,pid=%d\n",getpid());
sleep(1);
}
}
return 0;
}
终端查看进程信息 ps -ef
root 30214 0.0 0.0 4156 340 pts/2 S+ 14:16 0:00 ./a.out
root 30215 0.0 0.0 0 0 pts/2 Z+ 14:16 0:00 [a.out]
root 30235 0.0 0.1 139496 1648 pts/3 R+ 14:16 0:00 ps aux
有deaunct标记的进程为将死进程
子进程回收
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果时正常终止则保存退出状态,如果时异常终止则保留着导致进程终止的信号。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除这些进程。
回收子进程,用于知晓子进程死亡的原因
作用:
阻塞等待
回收子进程资源
查看死亡原因
1. wait(int *status)
函数原型:
pid_t wait(int *status)
参数:
status 传出参数,死亡原因
返回值:
成功返回正确的子进程ID
失败返回 -1
子进程死亡的原因:
1.正常死亡 WIFEXITED
如果WIFEXITED为真,使用WEXITSTATUS 得到退出的状态
- 非正常死亡WIFSIGNALED
如果WIFSIGNALED为真,使用WTERMSIG得到信号
2. waitpid
函数原型
pid_t waitpid(pid_t pid, int *status, int options);
参数:
pid :<-1 -组id , -1 回收任意子进程, 0 回收和调用进程组内的子进程, >0 会后指定的子进程pid
optons: 0 与 wait 相同,也会阻塞。 WNOHANG如果当前没有子进程退出会立即返回
status: 同上
返回值:
如果设置了WNOHANG,那么如果没有子进程退出,返回0. 如果有子进程退出,返回退出的pid
失败返回-1(没有子进程)
进程组可以通过 px -ef 来查看, 找到PGID那一列
[root@VM_0_11_centos ~]# ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 3:53 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 S 0 1:01 [ksoftirqd/0]
2 5 0 0 ? -1 S< 0 0:00 [kworker/0:0H]
一般父进程id就是组进程id
wait 函数示例代码:
#include
#include
#include
#include
#include
int main()
{
pid_t pid = fork();
if(pid == 0){
printf("I am child,will die!\n");
sleep(2);
while(1){
printf("I am child,guo lai da wo!\n");
sleep(1); //这里通过 kill 来杀掉这个进程,会走 WIFSIGNALED 里面的代码 status 为 9 即 signkill信号
}
//return 101; //return 退出认为是正常退出 会走 WIFEXITED status 为 101
exit(102); //如果走到这里,会走 WIFSIGNALED 里面的代码 status 为 101 即我们自己的异常退出码
}
else if(pid > 0){
printf("I am parent,wait for child die!\n");
int status;
pid_t wpid = wait(&status);
printf("wait ok,wpid=%d,pid=%d\n",wpid,pid);
if(WIFEXITED(status)){
printf("child exit with %d\n",WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("child killed by %d\n",WTERMSIG(status));
}
while(1){
sleep(1);
}
}
return 0;
}
waitpid 示例代码
#include
#include
#include
#include
#include
int main()
{
pid_t pid = fork();
if(pid == 0){
printf("I am child,pid=%d\n",getpid());
sleep(2);
}
else if(pid > 0){
printf("I am parent,pid=%d\n",getpid());
int ret ;
while((ret= waitpid(-1,NULL,WNOHANG) ) == 0 ){
// 没有子进程退出 返回0
sleep(1);
}
//子进程退出之后再waitpid 一次,就会返回错误 -1
printf("ret = %d\n",ret);
ret = waitpid(-1,NULL,WNOHANG);
if(ret < 0){
perror("wait err");
}
while(1){
sleep(1);
}
}
return 0;
}
wait回收n个子进程代码示例
#include
#include
#include
#include
#include
int main()
{
int n = 5;
int i =0;
pid_t pid;
for(i = 0 ;i < 5 ; i ++){
pid = fork();
if(pid == 0){
printf("I am child,pid=%d\n",getpid());
break;
}
}
sleep(i);
if(i == 5 ){
for(i = 0; i < 5; i ++){
pid_t wpid = wait(NULL);
printf("wpid = %d\n",wpid);
}
while(1){
sleep(1);
}
}
return 0;
}
waitpid 回收n个子进程
#include
#include
#include
#include
#include
int main()
{
int n =5;
int i ;
pid_t pid ;
for(i = 0; i < 5 ; i ++){
pid = fork();
if(pid == 0){
break;
}
}
if(i == 5){
//parent
printf("I am parent!");
//如何使用waitpid回收? -1 代表子进程都死了,都收了
while(1){
pid_t wpid = waitpid(-1,NULL,WNOHANG);
if(wpid == -1){
break;
}
else if(wpid > 0){
printf("waitpid wpid=%d\n",wpid);
}
}
while(1){
sleep(1);
}
}
if(i < 5){
// sleep(i);
printf("I am child,i =%d,pid=%d\n",i,getpid());
}
return 0;
}