孤儿进程与僵尸进程以及僵尸进程的解决

news2024/9/28 5:25:00

孤儿进程:

定义: 父进程运行结束,但子进程还在运行(未运行结束),这样的子进程就称为孤儿进程( Orphan Process )。
过程: 每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为  init init 进程会循环地 wait()  它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候, init   进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
这个 init 就是pid为1的进程。(实际情况可能因 特定的系统实现或环境而有所不同 )
由上面的过程可以得出结论: 孤儿进程没有什么危害
下面给出测试孤儿进程的测试代码:
 
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {

        printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());

    } else if(pid == 0) {
        sleep(1);
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}

执行结果如下:

我使用的是子系统,所以 init pid 可能不为1。 

观察上图也可以看到确实由 pid 为31的 init 进程。

还有一点需要注意的是:执行结果那里输出完父进程的结果,然后就将这个终端(父进程中输出的 ppid )切换到前台了。因为终端只知道父进程运行完了,不知道子进程还在运行,所以在输出子进程数据的时候终端前台出来了。(运行都是在后台的,输出在同一终端是因为父进程和子进程内核区有一些数据是共享的)

僵尸进程:

定义:

每个进程结束之后 , 都会释放自己地址空间中的用户区数据,内核区的 PCB 没有办法自己释放掉,需要父进程去释放
进程终止时,父进程尚未回收,子进程残留资源( PCB )存放于内核中,变成僵尸( Zombie )进程。
僵尸进程不能被 kill -9 杀死,这样就会导致一个问题,如果父进程不调用 wait() waitpid( ) 的话,那么 保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程,此即为僵尸进程的危害, 应当避免。

下面给出测试僵尸进程的测试代码:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {
        while(1) {
            printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
            sleep(1);
        }

    } else if(pid == 0) {
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}

执行结果如下:

 

打开另一个终端发现,子进程为僵尸进程,并且这个进程使用kill - 9是杀不掉的。

 如何解决僵尸进程:

进程回收:

1. 在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块 PCB 的信息(包括进程号、退出状态、运行时间等)。
2. 父进程可以通过调用 wait waitpid 得到它的退出状态同时彻底清除掉这个进程。
3. wait() waitpid() 函数的功能一样,区别在于, wait() 函数会阻塞, waitpid() 可以设置不阻塞, waitpid() 还可以指定等待哪个子进程结束。
注意:一次 wait waitpid 调用只能清理一个子进程,清理多个子进程应使用循环
退出信息相关宏函数:
WIFEXITED ( status ) 0 ,进程正常退出。
WEXITSTATUS ( status ) 如果上宏为真,获取进程退出的状态( exit 的参数)。
WIFSIGNALED ( status ) 0 ,进程异常终止。
WTERMSIG ( status ) 如果上宏为真,获取使进程终止的信号编号。
WIFSTOPPED ( status ) 0 ,进程处于暂停状态。
WSTOPSIG ( status ) 如果上宏为真,获取使进程暂停的信号的编号。
WIFCONTINUED ( status ) 0 ,进程暂停后已经继续运行。

wait():

下面给出解决僵尸进程的代码:
/*
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *wstatus);
        功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收子进程的资源。
        参数:int *wstatus
            进程退出时的状态信息,传入的是一个int类型的地址,传出参数。
        返回值:
            - 成功:返回被回收的子进程的id
            - 失败:-1 (所有的子进程都结束,调用函数失败)

    调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行)
    如果没有子进程了,函数立刻返回,返回-1;如果子进程都已经结束了,也会立即返回,返回-1.

*/
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>


int main() {

    // 有一个父进程,创建5个子进程(兄弟)
    pid_t pid;

    // 创建5个子进程
    for(int i = 0; i < 5; i++) {
        pid = fork();
        if(pid == 0) {// 子进程不需要继续产生子进程了
            break;
        }
    }

    if(pid > 0) {
        // 父进程
        while(1) {
            printf("parent, pid = %d\n", getpid());
            // int ret = wait(NULL);
            int st;
            int ret = wait(&st);// 获取子进程退出的状态
            if(ret == -1) {
                break;
            }
            if(WIFEXITED(st)) {
                // 是不是正常退出
                printf("退出的状态码:%d\n", WEXITSTATUS(st));
            }
            if(WIFSIGNALED(st)) {
                // 是不是异常终止
                printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
            }
            printf("child die, pid = %d\n", ret);
            sleep(1);
        }

    } else if (pid == 0){
        // 子进程
         while(1) {
            printf("child, pid = %d\n",getpid());    
            sleep(1);       
         }

        exit(0);
    }

    return 0; // exit(0)
}

 在测试正常退出的时候要记得把子进程中的while循环注释掉。

输出结果如下:

使用信号杀死子进程的输出结果为:

 

  

waitpid():

测试代码如下:

/*
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t waitpid(pid_t pid, int *wstatus, int options);
        功能:回收指定进程号的子进程,可以设置是否阻塞。
        参数:
            - pid:
                pid > 0 : 某个子进程的pid
                pid = 0 : 回收当前进程组的所有子进程  (一个进程是这个组的组长,那么这个进程组的pgid就是该进程的pid)  
                pid = -1 : 回收所有的子进程,相当于 wait()  (最常用)(在别的组的子进程也回收)
                pid < -1 : 某个进程组的组id的绝对值,回收指定进程组中的子进程
            - options:设置阻塞或者非阻塞
                0 : 阻塞
                WNOHANG : 非阻塞
            - 返回值:
                > 0 : 返回子进程的id
                = 0 : options=WNOHANG, 表示还有子进程活着(在非阻塞的情况下才有可能返回0)
                = -1 :错误,或者没有子进程了
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {

    // 有一个父进程,创建5个子进程(兄弟)
    pid_t pid;

    // 创建5个子进程
    for(int i = 0; i < 5; i++) {
        pid = fork();
        if(pid == 0) {
            break;
        }
    }

    if(pid > 0) {
        // 父进程
        while(1) {
            printf("parent, pid = %d\n", getpid());
            sleep(1);

            int st;
            // int ret = waitpid(-1, &st, 0);
            int ret = waitpid(-1, &st, WNOHANG);

            if(ret == -1) {
                break;
            } else if(ret == 0) {
                // 说明还有子进程存在
                continue;
            } else if(ret > 0) {

                if(WIFEXITED(st)) {
                    // 是不是正常退出
                    printf("退出的状态码:%d\n", WEXITSTATUS(st));
                }
                if(WIFSIGNALED(st)) {
                    // 是不是异常终止
                    printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                }

                printf("child die, pid = %d\n", ret);
            }
           
        }

    } else if (pid == 0){
        // 子进程
         while(1) {
            printf("child, pid = %d\n",getpid());    
            sleep(1);       
         }
        exit(0);
    }

    return 0; 
}

非阻塞执行结果如下:

由输出结果可知父进程没有阻塞在 waitpid() 那里,而是继续往下执行。 再使用 kill -9 信号杀死所有子进程,这时 waitpid() 返回-1,ret 接收-1终止死循环,整个程序就结束了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

放空一下自我 free

文章目录 放空一下自我 free默认的效果使用易读的参数间隔显示内存状态查看meminfo文件更多信息 放空一下自我 free **free**这个命令在Linux系统监控的工具里面&#xff0c;算是使用的比较多的一个。 使用_man_查看可知&#xff0c;官方含义为&#xff1a; Display amount o…

Dockerfile: CMD与ENTRYPOINT区别

CMD和ENTRYPOINT的作用 CMD和ENTRYPOINT这两个命令&#xff0c;我接触到的是用在了Dockerfile中用于构建容器。 CMD&#xff1a;The main purpose of a CMD is to provide defaults for an executing container. CMD的主要用途是为正在执行的容器提供默认值。也就是指定这个容…

D课堂 | 为什么网站搭建好了却无法访问?(上)

在上一期D课堂中&#xff0c;D妹教大家如何用最简单的方法快速搭建一个网站&#xff0c;相信很多小伙伴已经跃跃欲试&#xff0c;尝试去搭建自己的网站。&#xff08;点击这里可以快速复习&#xff09; 然而&#xff0c;有不少人明明每个步骤都跟着做了&#xff0c;但最后在浏览…

企业销售获客难?分享一个精准筛查企业客户的技巧

作为企业销售经理&#xff0c;曾经一直让我们很头疼的问题之一就是获客困难。回想起以往&#xff0c;我们需要通过各种手段&#xff0c;手动查找电话名单、网络搜索到各种渠道&#xff0c;费尽心思的去筛查才能找到潜在客户。获客流程长还耗费很多的精力&#xff0c;拿到手的客…

【test】wsl2和win互ping

参考&#xff1a; https://zhuanlan.zhihu.com/p/365058237 https://blog.csdn.net/Cypher_X/article/details/123011200

RTKlib操作手册--使用样例数据演示

简介 RTKLIB&#xff08;Real-Time Kinematic Library&#xff09;是一款开源的实时差分全球导航卫星系统&#xff08;GNSS&#xff09;软件库。它旨在提供高精度的位置解算&#xff0c;特别是在实时应用中&#xff0c;如精密农业、测绘、无人机导航等领域。 RTKLIB支持多种G…

复杂经济时期下的企业财务规划战略

多重危机、通货膨胀、外汇波动和市场变化的交汇给经济世界带来了前所未有的挑战&#xff0c;这使得企业对预测精准度和及时性的需求大大增加。平衡营收增长与成本输出的稳定性、在不断变化的市场行为中抓住商机提高盈利能力是现阶段财务专业人士必须掌握的技能。情景规划与财务…

Ubuntu下,Flutter安装及在VScode中的配置

1、安装flutter 在自己指定的目录下&#xff0c;新建文件夹&#xff0c;并将源码git clone到本地 $ mkdir flutter $ cd flutter$ git clone -b master https://github.com/flutter/flutter.git2、给flutter添加环境变量 #编辑配置文件 $ vi ~/.bashrc #在末尾加入以下内容&…

分布式系统中的CAP原理

分布式系统中的CAP原理 本文已收录至我的个人网站&#xff1a;程序员波特&#xff0c;主要记录Java相关技术系列教程&#xff0c;共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源&#xff0c;让想要学习的你&#xff0c;不再迷茫。 简介 在分布式系统中&…

rtklib读取原始数据是一次读取了一个文件的全部数据

一般来说&#xff0c;rtklib读取观测值文件&#xff08;o文件&#xff09;和导航文件&#xff08;n文件&#xff09;进行解算。 读取文件的时候&#xff0c;并非一次读取一个历元&#xff0c;而是将一个文件所有历元的数据都读取完毕以后&#xff0c;再进行解算。 这看起来是…

Go 如何处理死锁以提供哪些工具来检测或防死锁?

并发是 Go 的核心特性&#xff0c;它使程序能够同时处理多个任务。它是现代编程的一个强大组件&#xff0c;如果使用正确&#xff0c;可以产生高效、高性能的应用程序。然而&#xff0c;并发性也带来了顺序编程中不存在的某些类型错误的可能性&#xff0c;其中最臭名昭著的是死…

一般大家怎么部署java项目,要不要部署在docker里?

关于是否应该将Java项目部署在Docker中的思考 传统方式&#xff1a;直接在服务器上运行jar包&#xff0c;依赖于服务器的环境配置&#xff0c;可能会遇到环境不一致的问题。 Docker方式&#xff1a;通过容器化&#xff0c;你的应用和所有依赖都封装在一个Docker镜像中。部署时…

Portal认证

目录 一、Portal认证概述 1、802.1X和Portal对比 2、Portal认证流程 &#xff08;1&#xff09;、portal认证基本流程 二、MAC认证 1、MAC认证需求 2、MAC认证概述 3、MAC旁路认证 一、Portal认证概述 1、802.1X和Portal对比 802.1X认证要求认证终端安装专门的软件 8…

高效工作法:占位图片生成工具助力项目快速迭代

在现代设计和开发项目中&#xff0c;图片资源的重要性不言而喻。然而&#xff0c;项目中经常会遇到寻找合适图片、调整图片尺寸和格式等问题&#xff0c;这些问题不仅耗时耗力&#xff0c;还可能影响到项目的进度和质量。此时&#xff0c;占位图片生成工具应运而生&#xff0c;…

检索增强生成RAG

文章目录 RAG解释混合检索重排序Rerank为什么需要RerankHNSW带来的随机性问题 当前大模型处理长输入的水平依然不够大模型如何处理长输入&#xff1f;重要信息位置为什么会影响大模型的效果LangChain的解决方案-检索后重新排序文档 召回模式N选1召回模式多路召回模式 摘要 在RA…

黑马苍穹外卖学习Day6

HttpClient 介绍 HttpClient 是 Apache 提供的一个开源的 Java HTTP 客户端库&#xff0c;用于发送 HTTP 请求和处理 HTTP 响应。它提供了一种更简便的方式来执行 HTTP 请求&#xff0c;并支持多种协议&#xff0c;如 HTTP、HTTPS、FTP 等。 使用 HttpClient 可以方便地与远程…

Linux网络服务部署yum仓库

目录 一、网络文件 1.1.存储类型 1.2.FTP 文件传输协议 1.3.传输模式 二、内网搭建yum仓库 一、网络文件 1.1.存储类型 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff0…

多级缓存架构(五)缓存同步

文章目录 一、Canal服务1. mysql添加canal用户2. mysql配置文件3. canal配置文件 二、引入依赖三、监听Canal消息四、运行五、测试 通过本文章&#xff0c;可以完成多级缓存架构中的缓存同步。 一、Canal服务 1. mysql添加canal用户 连接在上一次multiCache项目中运行的mys…

从传统训练到预训练和微调的训练策略

目录 前言1 使用基础模型训练手段的传统训练策略1.1 随机初始化为模型提供初始点1.2 目标函数设定是优化性能的关键 2 BERT微调策略: 适应具体任务的精妙调整2.1 利用不同的representation和分类器进行微调2.2 通过fine-tuning适应具体任务 3 T5预训练策略: 统一任务形式以提高…

Mindspore 公开课 - GPT

GPT Task 在模型 finetune 中&#xff0c;需要根据不同的下游任务来处理输入&#xff0c;主要的下游任务可分为以下四类&#xff1a; 分类&#xff08;Classification&#xff09;&#xff1a;给定一个输入文本&#xff0c;将其分为若干类别中的一类&#xff0c;如情感分类、…