当前位置: 首页 > article >正文

【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;
}


http://www.kler.cn/news/290705.html

相关文章:

  • Django缓存
  • [论文笔记]Dimensionality Reduction by Learning an Invariant Mapping
  • 深入理解Java虚拟机的类加载机制
  • 无线通信中OFDM符号提前,有啥影响
  • linux 配置 iscsi 存储资源共享
  • html发送邮件的服务器怎么配置?如何设置?
  • GIT使用常见问题
  • 《JavaEE进阶》----7.<SpringMVC实践项目:【登录页面的验证】>
  • JVM:垃圾回收器 垃圾收集器分类 评估GC的性能指标
  • 双向链表
  • python办公自动化:使用`Python-PPTX`自动化与批量处理
  • 从零开始:理解并实践Prompt Flow
  • inotify + rsync 实时同步 ,定时备份
  • Android12上新增jar遇到的问题总结
  • fedora siliverblue adb
  • 为虚拟机配置固定的IP地址(CentOS9)
  • c++162 类的封装和访问
  • 转载【FIR 线性相位系统 最小相位系统 滤波器延迟】
  • 使用Dbeaver 操作 mongodb
  • 「小明赠书活动」第五期“网安三剑客”套系图书《内网渗透技术》《渗透测试技术》《Web应用安全》
  • Luminar Neo for Mac智能图像处理软件【操作简单,轻松上手】
  • LeetCode 热题100-11 滑动窗口的最大值
  • 前端防抖和节流函数的实现原理
  • MFC的控件无法触发事件函数(ON_COMMAND_RANGE的映射范围冲突)
  • 百度翻译API翻译Qt LinguistTools的ts文件
  • 百度飞浆目标检测PPYOLOE模型在PC端、Jetson上的部署(python)
  • React 创建和嵌套组件
  • 策略规划:在MySQL中实现数据恢复的全面指南
  • [Python图论]在用图nx.shortest_path求解最短路径时,节点之间有多条边edge,会如何处理?
  • 【MySQL】索引使用规则——(覆盖索引,单列索引,联合索引,前缀索引,SQL提示,数据分布影响,查询失效情况)