【Linux】僵尸进程(第十一篇)

目录

1.僵尸进程概述

2.回收僵尸进程

1.wait()函数

2.waitpid()函数


1.僵尸进程概述

由一个例子引入:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
​
int main()
{
    pid_t pid = fork();
    if(pid >0){
            printf("parent process \n");
            while(1) sleep(1);
     }
     else if(pid ==0){
              printf("child  process \n");
              exit(0);
     }else
     {
            perror("fork failed");
            exit(0);
     }
}
​

这份代码子进程通子进程先于父进程终止。这种内存泄漏由僵尸进程导致的(残留的子进程PCB)【思考:为什么会残留】 exit里面调用_exit() 进程结束时负责释放进程资源 将0-3G都释放,内核空间释放部分PCB残留此为僵尸进程。除非父进程通过wait回收僵尸进程。

2.回收僵尸进程

1.wait()函数

wait()阻塞函数,子进程存在,但未产生僵尸,wait阻塞等待,产生僵尸后立即回收。

注意:wait每调用一次只能回收一次

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
​
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
     pid_t pid = fork();
     if(pid >0)
      {
            printf("parent process pid [%d]\n",getpid());//pid =getpid() 获取当前进程id
                //pid = getppid() 获取父进程的id
            pid_t zid = wait(NULL); //等待子进程结束回收,释放残留pcb,即僵尸进程
            printf("child process pid [%d]",zid);
      }else if(pid ==0)
      {
            printf("child process\n");
            exit(0);
      }
      while(1)sleep(1);
    return 0;
}

wait返回值:1.回收成功,wait返回值僵尸进程的pid 2.回收失败,wait 返回-1(什么时候失败? .没有子进程)

为什么不回收干净呢?怕父进程出现野id (比如 张三孩子死了还直接处理了,父亲不知道),父进程通过pcb 知道子进程的终止信息 父进程通过子进程的终止信息,可以知道子进程的退出原因,如果是正常终止,退出码可以获取到,如果是一场终止,例如被别人杀死,可以获取杀死进程的信号编号以及其进程的id wait(int *status) 对status进行校验就可以获悉详细的子进程退出信息 常见的子进程退出原因 1.子进程正常退出(exit、return) 2.异常退出(硬件错误异常进程终止,其他进程利用信号杀死) 3.进程被人为挂起 一个整型里面要存储20个option,每一个选项对应一位,0or1表示开启或关闭,位域 比如open(path,o_read|o_write|o_create) 用于校验status的一系列函数: WIFEXITED(status) #判断子进程是否正常退出,正常退出返回真 WEXITSTATUS(status) #该函数从status提取退出码并返回

WIFSIGNALED(status) #判断子进程是否是被信号杀死的,如果是则返回真 WTERMSIG(status)#该函数可以从status提取 终止子进程的信号编码并返回

//wait中的status 可以查看子进程的退出原因

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
​
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
     int status;
     pid_t pid = fork();
     if(pid >0)
      {
            printf("parent process pid [%d]\n",getpid());
            pid_t zid = wait(&status);
            if(WIFEXITED(status))
              {
                    printf("parent process wait successfully,zombie [%d] exit code [%d] \n",getpid(),WEXITSTATUS(status));
​
              }
             else if(WIFSIGNALED(status))
              {
​
                    printf("parent process wait successfully,zombie [%d] signal [%d] \n",getpid(),WTERMSIG(status));
              }
      }else if(pid ==0)
      {
            printf("child process\n");
            //exit(0); //正常退出
            while(1) sleep(1);//信号退出 kill -2  pid
      }
      while(1)sleep(1);
    return 0;
}

注意printf输出必须要加\n结束,否则输出不出来。

2.waitpid()函数

waitpid 回收函数 (比wait功能性更强,回收更为灵活) waitpid(pid_t pid,int* status,int option);

1)pid:可以通过该参数指定waitpid回收

小于-1 跨组回收,指定组id,回收指定进程组种的(僵尸)子进 程,-5000(指定组id) ​ -1 回收任意子进程if ​ 0 同组回收,父进程只能回收与自己同组的子进程

大于0 点名回收,指定子进程的pid,回收子进程3000 前面三个可以回收多个,>0回收一个 2)status 回收成功,传出子进程的退出信息,无论是wait还是waitpid,int* status 可以传NULL,不接收子进程退出信息

3)options: 可以通过选项参数,设计waitpid的工作状态以及回收方式

WNOHANG=可以通过该选项,将waitpid设置为非阻塞回收,没有子进程退出立即返回:

waitpid 返回值: 1)回收成功,子进程id

2)回收失败,wait返回-1 (没有子进程) ​ 3)非阻塞回收,返回0,表示当前的子进程没有结束,无需回收

阻塞回收策略 和非阻塞回收策略 阻塞回收:阻塞工作需要挂起唤醒,如果挂起唤醒频繁,增大系统开销 非阻塞回收: 无挂起唤醒开销 阻塞回收:阻塞回收会对父进程造成一定影响,父进程自身的任务被搁置 无法进行推进 非阻塞回收:轮询回收,定时查看子进程是否需要回收,如果不需要返回执行自己的任务,如果需要则回收释放PCB残留 这两种回收策略都不完善,都会对父进程核心工作造成影响

示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void Parent_job()
{
    printf("Parent job..id [%d]...\n",getpid());
}
int main()
{
      pid_t zid;
      pid_t pid = fork();
      if(pid >0)
       {
            while((zid = waitpid(-1,0,WNOHANG))!= -1)
            {
                   if(zid ==0)
                    {
                        Parent_job();
                    }else if(zid >0)
                    {
                         printf("parent process wait child process %d\n",zid);
                    }
                    sleep(2);//
            }
       }else if(pid ==0)
       {
              printf("child process id [%d]\n",getpid());
              sleep(5);
              exit(0);
       }else
       {
              perror("fork failed\n");
              exit(0);
       }
      return 0;
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 中,僵尸进程和孤儿进程都是指与父进程不再有联系的进程,它们通常是由于进程管理不当或程序错误导致的。 **僵尸进程**是已经完成执行任务,但其父进程还没有来得及处理其退出状态的进程。当进程完成执行后,它的退出状态(也称为退出码或终止状态)会被保存在系统中,直到父进程通过 `wait` 或 `waitpid` 等函数来获取该状态。如果父进程没有处理该状态,那么该进程就会成为僵尸进程,占用系统资源。要清理僵尸进程,可以使用 `kill` 命令向其父进程发送 `SIGCHLD` 信号,或者重新编写程序,使其正确处理子进程的退出状态。 **孤儿进程**是指其父进程已经退出或被终止,但其自身仍在运行的进程。孤儿进程会被 `init` 进程(进程号为 `1`)接管,`init` 进程会定期检查系统中是否有孤儿进程,并且将其的父进程设置为 `init` 进程。要避免孤儿进程的产生,可以在父进程退出之前,等待子进程的退出,或者将子进程的父进程设置为 `init` 进程。 可以使用 `ps` 命令来查看系统中的僵尸进程和孤儿进程。使用以下命令可以查看所有僵尸进程: ``` ps aux | grep 'Z' ``` 其中,`aux` 选项用于显示所有进程,`grep 'Z'` 用于查找所有状态为 `Z` 的进程,即僵尸进程。 使用以下命令可以查看所有孤儿进程: ``` ps -ejH ``` 其中,`-e` 选项用于显示所有进程,`-j` 选项用于以层次结构的形式显示进程,`-H` 选项用于显示所有孤儿进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的小猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值