【Linux】详解进程状态之僵尸进程——孤儿进程

目录

🌞专栏导读

🌛什么是进程

⭐什么是PCB? 

🌛查看进程 

🌛如何通过系统调用查看进程PID

🌛fork

🌞认识进程状态

🌛查看进程状态 

🌛R状态

 ⭐例如:

🌛S状态 

🌛D状态 

🌛T状态

🌛t状态:

🌛X状态 

🌛Z状态

⭐僵尸进程

⭐僵尸进程的危害 

🌛孤儿进程


🌞专栏导读

🌟作者简介:日出等日落,在读本科生一枚,致力于 C/C++、Linux 学习。

🌟本文收录于 Linux系列,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,持续更新!

🌟相关专栏推荐:C语言系列 、Linux系列 、数据结构与算法

🌛什么是进程

有小伙伴就会问了,什么是进程呢?

进程=内核关于进程的相关数据结构+当前代码的内容和数据

什么是进程?


早期的计算机一次只能执行一个程序,这种程序完全控制系统,并且访问所有系统资源。到了现代,计算机系统允许加载多个程序到内存,以便于并发执行。这就要求操作系统对各种程序提供更严格的控制和更好地划分和规划。这些需求引发了进程概念的产生,大白话来说,进程就是正在执行的程序,是现代分时操作系统的工作单元。
操作系统的复杂程度决定它可以为用户带来更好地体验感。虽然它主要关注的是执行用户程序,但是它也要顾及各种系统任务。因此系统会由一组进程组成,操作系统进程和用户进程;操作系统进程执行系统代码,而用户进程执行用户代码。
通过 CPU 的多路复用,所有这些进程可以并发执行。通过在进程之间切换 CPU,操作系统能使计算机更为高效。

前面说,进程是执行的程序,这是一种非正式的说法。进程不只是程序代码(文本段或代码段),通常还包含以下内容:

  1. 当前活动,如程序计数器的值和处理器寄存器的内容等。
  2. 进程堆栈(包括临时数据,如函数参数、返回地址和局部变量)和数据段(包括全局变量)。
  3. 堆,这是在进程运行时动态分配的内存。

⭐什么是PCB? 

从操作系统理解进程概念-------先描述,后组织
为了使参与并发执行的程序能独立的运行,必须为之配置一个专门的数据结构-----task_struct,称为进程控制块(PCB进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程(也就是组织)进程。 相应地,由程序段、相关数据段和PCB三部分构成了进程映像(进程实体)。所谓创建进程,实质上是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。值得注意的是,进程映像是静态的,进程则是动态的。
进程的唯一标志—PCB
综上所述,进程的定义大体分为以下几点

  • 进程是程序的一次执行
  • 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
  • 进程是具有独立功能的程序在一个数据集合上运行过程,它是系统进行资源分配和调度的一个独立单位

进程的特征

  • 进程是由多程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

动态性:进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。
并发性:指多个进程实体,同存于内存中,能在一段时间内同时运行,并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是为了使程序能与其他进程 的程序并发执行,以提高资源利用率。
独立性:指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序都不能作为一个独立的单位参与运行。
异步性:由于进程的相互制约,使进程具有执行的间断性,即进程按各自独立的、 不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此,在操作系统中必须 配置相应的进程同步机制。
结构性:每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制段三部分组成的。
进程的状态和转换

task_ struct内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]
  • IO状态信息: 包括显示的I/O请求,分配给进程的IO设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

🌛查看进程 

#include<stdio.h>int main()
{while(1){printf("我是一个进程\n");sleep(1);}return 0;
}
  • 这是一个简单的进程(myprocess)
ps axj | head -1 && ps axj | grep myprocess | grep -v grep
  • 这是查看进程的指令!

🌛如何通过系统调用查看进程PID

我们还可以用系统调用getpid()获取当前进程的pidgetppid()获取当前进程的父进程的PID

首先通过man手册查询这两个函数如何使用:

$ man getpid

运行截图: 

 注意,返回值类型未pid_t其实就是size_t。通过一下代码进行测试:

#include<stdio.h>
#include<sys/types.h>int main()
{while(1){printf("我是一个进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}return 0;
}

运行截图: 

🌛fork

首先我们通过man手册认识一下fork

$ man fork

运行截图: 

 简单说明就是fork用来创建子进程,在父进程中,fork的返回值为子进程的PID;在子进程中返回值为0。

#include<stdio.h>
#include<sys/types.h>int main()
{printf("fork调用之前的内容.....\n");fork();printf("fork调用之后的内容.....\n");return 0;
}

运行截图: 

我们可以看出这里fork调用之后的内容打印了两遍。原因是当调用fork之后,子进程被创建,父进程与子进程同时在运行,于是父进程打印了一遍fork调用后的内容,子进程也打印了一遍fork调用后的内容。

fork的功能很强大,我们一般需要与if配合使用进行分流。还记得上面提到的fork的返回值吗?子进程返回值为0,父进程返回值为子进程PID,可以此作为分流的依据。例如:

#include<stdio.h>
#include<sys/types.h>int main()
{pid_t ret = fork();if(ret==0){// 子进程while(1){printf("我是子进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}}else {// 父进程while(1){printf("我是父进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());sleep(1);}}return 0;
}

运行截图:

此刻父子进程都在运行。那么问题来了——

  • 请问为什么此时ifelse竟然能够同时执行?也就是fork为什么会有两个返回值

 进程间是互相独立的

例如qq与微信同时运行,两个并无关联,互不影响。

我们知道进程=内核数据结构(PCB)+代码和数据。当子进程创建时,操作系统会为子进程创建一个PCB记录子进程的状态信息等。同时子进程会与父进程共同使用一份代码和数据,若有任意一个执行流(只父子进程)修改数据时,操作系统会为该进程将代码和数据拷贝一份,再进行修改,此动作我们称之为写时拷贝,写时拷贝同样为非常重要的知识。

最后,因为进程具有独立性,同样父子进程也具有独立性,且由于写时拷贝的存在,父进程中调用fork会返回子进程的PID,所以else执行了,这是父进程的行为;子进程中会fork会返回0,所以if执行了,这是子进程的行为。父子进程互不影响

🌞认识进程状态

进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,使得进程的状态也在不断地发生变化(一个进程会经历若干种不同状态)。通常进程有以下五种状态,前三种是进程的基本状态。

进程转换图

  • 运行状态:进程正在处理机上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
  • 就绪状态:进程已处于准备运行的状态,即进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行。
  • 阻塞状态:又称等待状态:进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。
  • 创建状态:进程正在被创建,尚未转到就绪状态。创建进程通常需要多个步骤:首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后由系统为该进程分 配运行时所必需的资源;最后把该进程转入到就绪状态。
  • 结束状态:进程正从系统中消失,这可能是进程正常结束或其他原因中断退出运行。当进程需要结束运行时,系统首先必须置该进程为结束状态,然后再进一步处理资源释放和 回收等工作。

Linux中进程状态一般有:

  • R(运行状态):并不意外着真正的在运行(指正在被CPU调度);
  • S(休眠状态):进程在等待获取某种资源,此状态还被称为可中断休眠;
  • D(磁盘休眠状态):在这个状态的进程也是在休眠,但是不可被中断,因此又称过该状态为不可中断休眠;
  • T(暂停状态):可以通过发送 SIGSTOP 信号给进程来停止进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X(死亡状态):这个状态只是一个返回状态,你不会在任务列表里看到这个状态;
  • Z(僵尸状态):当一个子进程没有被父进程“回收”,该进程就会处于僵尸状态;

 

🌛查看进程状态 

指令:

ps axj | head -n1 && ps axj | grep myprocess | grep -v grep

🌛R状态

当你在电脑上同时运行很多程序,例如你敲代码的时候,还听着某个软件播放的歌曲,或者在浏览器之间来回切换。请问此时这些所有的应用都在CPU运行吗?

答案是,并不是这样的。

在CPU进行工作的时候,会存在一个进程运行的队列。队列维护的内容是一个个task_struct结构体的指针。在该队列中维护的进程都处于R状态,且等着被CPU所调度。

 ⭐例如:

根据上图,问题来了,为什么在该程序执行时,并没有看到所谓的R状态呢?


原因是由于CPU运算速度太快了,我们基本很难看到R状态。该进程死循环的在屏幕上打印。我们都知道此时的屏幕是一种外设,而CPU的计算速度相比较外设的访问速度根本不在一个量级。所以,该进程死循环的在屏幕上打印内容,有99.9%的时间都在访问外设,剩下的时间是CPU在做计算。在进程访问外设的时候,CPU并不会傻傻的原地等待,而是转头却做别的事,当该进程访问外设成功后,CPU再对它进行调度。

那么有什么办法等看到R状态呢?我们将上面的代码略作修改:

#include<stdio.h>
#include<unistd.h>int main()
{while(1){//printf("hello myprocess\n");}return 0;
}

 运行截图:

 如上图所示,当我们不再访问外设,而是只不停地做重复的运算,此时CPU会一直被调度,就能看到R状态了。

🌛S状态 

S状态称为休眠状态。休眠状态本质是一种阻塞。

阻塞:进程因为等待某种资源就绪而表现出的不推进的状态。

例如,当一个进程运行到一半,需要从磁盘上获取很大的一块数据,那么就要花费较久的时间。此时OS的处理方式是,让该进程继续等待它要的数据,但是要求你不能在等待资源的时候还占用着CPU,于是该进程就被OS安排到某个地方进行等待,这时该进程就处于S状态。


运行截图

如上图所示,当进程等待用户从键盘上输入的数据时,它就处于睡眠状态。 

🌛D状态 

D状态也是一种休眠状态,但是它又有个名字叫做磁盘休眠状态或者不可中断休眠。那么如何看待S状态与D状态的区别呢?

🌛T状态

T状态称为停止状态,就是让某个进程暂停一下。

1 #include<stdio.h>2 #include<unistd.h>3 4 int main()5 {6   int count = 0;7    while(1)8    {9      //printf("hello myprocess\n"); 10      printf("我再运行吗??%d\n",count++);11      sleep(1);                                           12    }13    return 0;14 }

当程序开始运行后,此时向进程发送暂停的信号:

$ kill -18 (进程PID)

运行截图: 

此外,我们还可以发送继续的信号让该进程继续执行:

$ kill -18 (进程PID)

 运行截图:

 注意:

进程继续在运行了。但是我们发现有一个地方好像和之前不一样了,S后面是不是一直有一个+号来着?我们也不知道+是干嘛的,只知道他现在好像消失了。

“+” 代表在前台运行,没有”+“表示在后台运行;


之前我们在终止一个程序时,习惯使用Ctrl + c ,但是现在好像对于后台在运行的进程失效了,此时我们需要掌握一条新的指令来”杀掉“进程:

$ kill -9 (进程PID)

🌛t状态:

 例如在调试时,我们设置了几个断点。当进程在该断点处停下来时,该进程就处于暂停状态。

 

 

可以看见我们在第十行打了断点,此时观看进程状态:

 我们在调试中运行一下:

观看运行状态:

🌛X状态 

  • X状态为死亡状态是一个瞬时状态不易观察,暂且认为它不重要;

🌛Z状态

  • Z状态被称为僵尸状态。顾名思义,一个进程死了(退出了)但没有”收尸“,就成了”僵尸“。具体一点,当一个进程退出时如果它的父进程没有读取到该进程退出时返回的退出状态码,该进程就会变成僵尸进程。

⭐僵尸进程

#include<stdio.h> 
#include<unistd.h>int main() 
{pid_t id = fork();if(id == 0){while(1){printf("我是子进程,我在运行,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}}else if(id > 0) {while(1){printf("我是父进程,我在运行,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}}return 0;
}

当我们运行程序后,能看到程序正常的在运行; 

 此时当我们执行指令将子进程”杀“掉,子进程就会变成僵尸进程;

$ kill -9 (子进程PID)

其中我们能看到一个英文单词——defunct就是僵尸的意思。 

⭐僵尸进程的危害 

  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(即PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护
  • 一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存。

🌛孤儿进程

所谓孤儿进程,故名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。

运行该程序,我们使用kill命令”杀“掉父进程,此时再来查看进程信息:

如上图所示,子进程发生了两个变化。一是子进程的PPID,二是子进程变为在后台运行了。 

当子进程的父进程挂掉之后,子进程会被1号进程领养。该进程也被称为孤儿进程。

  • 那么为什么要进行领养呢?

原因是孤儿进程会被init进程(1号进程)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害.

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware Workstation及CentOS-7虚机安装

创建新的虚机&#xff1a; 选择安装软件&#xff08;这里选的是桌面版&#xff0c;也可以根据实际情况进行选择&#xff09; 等待检查软件依赖关系 选择安装位置&#xff0c;自主配置分区 ​​​​​​​ 创建一个普通用户 安装完成后重启 点击完成配置&#xff0c;进入登陆界面…

怎么进行流程图制作?用这个工具制作很方便

怎么进行流程图制作&#xff1f;流程图是一种非常有用的工具&#xff0c;可以帮助我们更好地理解和展示各种复杂的业务流程和工作流程。它可以将复杂的过程简化为易于理解的图形和文本&#xff0c;使得人们更容易理解和跟踪整个流程。因此&#xff0c;制作流程图是在日常工作中…

拆解与重构:慕云游首页组件化设计

目录 前言1 项目准备1.1 创建项目目录1.2 搭建项目开发环境 2 项目组件化2.1 在当前环境启动原有项目2.2 顶部组件2.3 幻灯片组件2.3.1 功能实现2.3.2 加载中组件2.3.3 结构和样式2.3.4 使用Ajax获取数据 2.4 机酒自由行组件2.5 拆分余下的css文件 3 项目完善4 源码 前言 在现代…

如何使Python Docker镜像安全、快速、小巧

一、说明 在微服务领域&#xff0c;拥有安全、高效和紧凑的 Docker 映像对于成功部署至关重要。本博客将探讨有助于构建此类映像的关键因素&#xff0c;包括不以 root 用户身份运行映像的重要性、在构建映像时更新和升级包、在编写 Dockerfile 指令时考虑 Docker 的层架构&…

岩土工程仪器多通道振弦传感器信号转换器应用于桥梁安全监测

岩土工程仪器多通道振弦传感器信号转换器应用于桥梁安全监测 桥梁作为交通运输的重要节点&#xff0c;其安全性一直备受关注。不同于其他建筑物&#xff0c;桥梁所处的环境复杂多变&#xff0c;同时&#xff0c;其所需承受的负荷也相对较大&#xff0c;这就需要对桥梁的安全进…

Windows 安装 pandoc 将 jupyter 导出 pdf 文件

Windows 安装 pandoc 将 jupyter 导出 pdf 文件 1. 下载 pandoc 安装文件2. 安装 pandoc3. 安装 nbconvert4. 使用 pandoc 1. 下载 pandoc 安装文件 访问 https://github.com/jgm/pandoc/releases&#xff0c;下载最新版安装文件&#xff0c;例如&#xff0c;3.1.6.1 版&#…

常见分布式ID解决方案总结:数据库、算法、开源组件

常见分布式ID解决方案总结 分布式ID分布式ID方案之数据库数据库主键自增数据库号段模式Redis自增MongoDB 分布式ID方案之算法UUIDSnowflake(雪花算法) 雪花算法的使用IdWorker工具类配置分布式ID生成器 分布式ID方案之开源组件uid- generator(百度)Tinyid&#xff08;滴滴&…

基于Java的体育网站的设计与实现(论文+源码)_kaic

基于Java的体育网站的设计与实现 摘 要&#xff1a;在网络应用的迅速发展与科技的不断进步的现代环境下&#xff0c;人们生活节奏越来越快&#xff0c;娱乐方式也多种多样&#xff0c;各种软件应用&#xff0c;各种娱乐&#xff0c;购物网站已经成为必不可少的伴随品&#xff…

《24海南大学835软件工程考研经验贴》

1.经验之谈 首先&#xff0c;我是一个二战的考生&#xff0c;一战给我带来的经验有几点。第一&#xff0c;数学、专业课这两门越早复习越好&#xff0c;越拖到后面你就会发现来不及了&#xff0c;这学不完&#xff0c;那学不完的。第二、我认为是比较关键的一点&#xff0c;一定…

Idea创建maven管理的web项目

如果你想在项目中添加一个传统的 src 目录来存放源代码&#xff0c;可以按照以下步骤操作&#xff1a; 1. 在项目视图中&#xff0c;右键单击项目名称&#xff0c;选择 “New” -> “Directory”。 2. 在弹出的对话框中&#xff0c;输入目录名称为 “src”&#xff0c;然后…

使用ffmpeg将m4a及wav等文件转换为MP3格式

要使用ffmpeg将m4a及wav等文件转换为MP3格式&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装 ffmpeg 确保您已经安装了ffmpeg软件。如果没有安装&#xff0c;请访问ffmpeg的官方网站https://ffmpeg.org/ 并按照说明进行安装。 Win10 / Win11 可以通过 winget 命令…

Vue自定义指令使用

本篇文章讲述使用Vue自定义指令&#xff0c;并在项目中完成相应功能。 在平常Vue脚手架项目中&#xff0c;使用到 自定义指令较少&#xff0c;一般都是使用的自带指令&#xff0c;比如 v-show 、v-if 、 v-for 、 v-bind 之类的。这些已经能够满足大多数项目使用。更多的可能也…

蚁剑antSword-maste下载-安装-使用-一句话木马

下载 https://github.com/AntSwordProject/antSword 一句话木马 hack.php脚本 <?php eval($_POST[attack]);?> 安装 1、安装完成后启动 2、初始化&#xff0c;选择有源码的目录 3、连接

SpringMVC的架构有什么优势?——视图与模型(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

c语言每日一练(2)

前言&#xff1a; 每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

leetcode 63. 不同路径 II

2023.8.9 这题是不同路径I的升级版&#xff0c;在路径上增加了障碍物&#xff0c;有障碍物的地方无法通过。 我的思路依然还是使用动态规划&#xff0c;dp[i][j]的含义依然是到&#xff08;i&#xff0c;j&#xff09;这个位置的路径个数。只需要在dp数组中将有障碍物的地方赋为…

js案例:1.简单计算器

目录 一.效果图 二.实现思路 整体思路 ​ 1.关键是dom操作 ​ 2.设置点击事件 3.数据类型的隐式转换和赋值 三.完整代码 一.效果图 二.实现思路 整体思路 1.关键是dom操作 通过 document.getElementById(id) 获取html中的dom元素 每一个html标签都是一个对象&…

CANoe通过Frame Histogram窗口统计报文周期(方便快捷)

文章目录 效果展示1.插入Frame Histogram窗口2.Activate3.运行CANoe&#xff0c;停止后查看write窗口 效果展示 统计报文周期信息输出在write窗口。 1.插入Frame Histogram窗口 2.Activate 3.运行CANoe&#xff0c;停止后查看write窗口 统计报文周期信息输出在write窗口。

Pytorch深度学习-----神经网络模型的保存与加载(VGG16模型)

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

数据结构刷题训练——链表篇(一)

目录 前言 题目一&#xff1a;链表的中间节点 思路 分析 题解 题目二&#xff1a;链表中倒数第k个结点 思路 分析 题解 题目三&#xff1a;合并两个有序链表 思路 分析 题解 方法二 题解 题目四&#xff1a;链表的回文结构 思路 分析 题解 总结 前言 今天我将开…