【Linux从入门到放弃】进程状态的理解以及什么是僵尸进程和孤儿进程?

news2024/9/29 9:29:55

🧑‍💻作者: @情话0.0
📝专栏:《Linux从入门到放弃》
👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!

在这里插入图片描述

文章目录

  • 前言
  • 一、进程状态
    • 1.1 阻塞和挂起
    • 1.2 进程只要是R状态,就一定是在CPU上运行吗?
      • 1.2.1 这样的进程处于R状态吗?
      • 1.2.1 为什么这样的进程处于R状态?
    • 1.3 休眠状态
    • 1.4 暂停状态
    • 1.5 僵尸状态(僵尸进程)
  • 二、孤儿进程
    • 2.1 什么是孤儿进程?
    • 2.2 孤儿进程周边
  • 总结


前言

此篇博客依然是在之前的学习基础之上继续探究关于进程的知识。


一、进程状态

  首先大家可以思考一个问题,当电脑打开一个客户端程序,它是一直在被运行吗?

  答案当然不是,一个进程是不可能被一直运行的,在计算机内部都是多个进程来回被调度的,之所以我们感觉它一直在运行是因为进程之间的切换速度太快了,人是感受不到切换带来的延迟。

  进程在运行的时候是可以被操作系统管理、调度的,这里就有一个问题,为什么这个进程被调度呢?为什么不调度其他进程呢?而这就取决于进程状态

1.1 阻塞和挂起

要理解进程状态咱们先看一下这两种状态:阻塞和挂起

阻塞:进程因为等待某种条件就绪而导致的一种不推进的状态,其实就是我们常说的卡住了。阻塞一定是在等待某种资源,那么进程卡住了其实就是等待着CPU来调度它,站在进程的角度,它就是在等待获取到CPU的资源。所以说为什么阻塞呢?进程就是通过等待的方式,等具体的资源被别的进程使用完之后给自己拿到。
阻塞——进程等待某种资源就绪的过程。

  但是,进程是怎么等待的呢?
  这里提到的资源不仅有软件资源,也有硬件资源(磁盘、网卡、显卡等外设)。操作系统不仅要对进程管理,也得对这些资源做管理——先描述,再组织。于是也得为每个资源创建对应的结构体并链接起来。假设说某个进程现在缺少网卡这个硬件资源,那么操作系统就会将这个进程的PCB与网卡资源的结构体链接起来,当网卡资源被该进程拿到时,就会将该进程的PCB重新链接到被CPU调度的执行队列中。

挂起:在阻塞的概念中,若一个进程因没有某种资源就会被链接到对应资源的结构队列或链表中,而这些东西(进程PCB、代码数据、资源结构队列)都是存放在内存中的,如果这样的进程太多的话就会一直占据着内存的空间资源造成浪费,为了避免这种浪费操作系统将那些长时间不会被调度的进程的代码数据转移到磁盘中,这样就释放了内存的空间。而那些代码数据被转移到磁盘上的进程就进入了挂起状态。

下面将学习关于Linux操作系统的进程状态变化:

static const char * const task_state_array[] = {
"R (running)", /*0*/
"S (sleeping)", /*1*/
"D (disk sleep)", /*2*/
"T (stopped)", /*4*/
"t (tracing stop)", /*8*/
"X (dead)", /*16*/
"Z (zombie)", /*32*/
};

  task_struct是一个结构体,内部包含这关于进程的各种属性,其中一个就是状态。进程状态是通过一个数组来表示的,用不同的数字代表不同的状态。

1.2 进程只要是R状态,就一定是在CPU上运行吗?

  不一定,一个进程现在处于R状态并不代表你一定在CPU上运行。也就是说,系统中可能会存在多个进程是处于R状态的,但是可能只有那么几个在CPU上运行,所以CPU在调度进程时也一定要维护一个运行队列——将所有待运行的进程维护成一个队列。当CPU要调度进程时就直接从这个队列中挑选进程即可。同时要说明一个进程是什么状态一般也看这个进程在哪里排队。

1.2.1 这样的进程处于R状态吗?

#include <stdio.h>

int main()
{
	while(1)
	{  
		printf("我在运行吗???\n");
	}
	return 0;
}

运行上面的代码发现它陷入了死循环,会不停的给显示器上打印数据。那它是处于R状态吗?
经过查看之后发现该进程状态并不是R,而是S+,该进程不是跑得很欢实吗,为甚么不是R状态呢?

在这里插入图片描述

  这是因为当执行打印语句得时候是需要访问显示器这样的外设资源,但是外设资源不可能直接被这个进程申请到,因为它还有可能被其他进程所访问,因此该进程就得需要在这个外设等待队列上进行等待,而CPU的执行速度非常快,CPU不允许你在持有CPU资源的同时还去等待某种资源,因此当查看进程状态的时候大部分就是S状态。
  可是这个进程总有在CPU运行的时候吧,那我应该能查到R状态吧,可是怎么好像一直查不到呢?因为代码很短,一瞬间就被执行完了,而打印相对比较慢,可能1s的时间 99.9% 都是在等待的,所以在查看时可能得上万次才能查到一次R状态。

1.2.1 为什么这样的进程处于R状态?

  当把那一行打印语句注释掉之后就会发现该进程又处于R状态,很明显,问题出现在那一行打印语句上。

在这里插入图片描述

  而将那行代码注释掉,该进程就处于R状态,这是因为不用访问外设,while循环就是个判断语句,是纯计算的代码。在整个进程调度的生命周期里只会用CPU资源,只要被调度就一直是R状态,只要被查看就一直是R状态。
  归根结底就是CPU太快了。

1.3 休眠状态

  在上面的内容我们已经了解了S状态,表示可中断休眠,本质就是一种阻塞状态。
  而D休眠状态表示的是不可中断休眠,但是对于我们来说一般用不着。对于D状态举个简单例子解释一下:
  假设内存中某个进程有1GB的数据要写到磁盘上,于是在与磁盘交涉之后就开始往磁盘上写,毕竟磁盘的读写速度较慢,因此这个进程的状态被设置为阻塞状态,然后在磁盘的内核数据队列中等待排队。可是就在磁盘兢兢业业的拷贝数据的时候操作系统路过发现内存都忙成什么样了,怎么还有进程在这啥也不干还占着空间,于是就杀掉了这个进程。之后呢磁盘因为某种原因导致写失败,于是磁盘就要去找那个进程,可是发现怎么不见了,这就会导致数据的丢失。
  那这个后果到底是谁的锅呢?磁盘?内存还是操作系统呢?其实都不是它们的锅,而是操作系统设计者的锅。实际上只要保证这个进程不能被杀死即可。于是就有了D状态。

1.4 暂停状态

  T表示暂停状态,可通过指令的方式将一个进程的状态改为T状态。

#include <stdio.h>
#include <unistd.h>
int main()
{
	int count=0;
	while(1)
	{  
		printf("我再运行吗??? count:%d\n",count++);
		sleep(1);
	}
	return 0;
}

运行上面的代码我们知道会一直打印并且该进程的状态基本上都是S状态,我们可以通过发送19号信号(SIGSTOP)来暂停该进程,然后可再通过18号信号(SIGCONT)恢复该进程。

  1. 暂停进程
    在这里插入图片描述
  2. 恢复进程
    在这里插入图片描述
    此时发现进程又像之前一样开始打印数据了,只是该进程的状态没有之前的 ‘+’,除此之外就是发现ctrl+c 并不能结束掉该进程。这是因为如果状态带了 ‘+’ 表示在前台运行,前台进程你可以ctrl+c 终止进程。但没有 ‘+’ 表示后台运行,就像上面代码一样一直打印数据,此时要想杀掉该进程就可通过9号信号杀死该进程。
    在这里插入图片描述

1.5 僵尸状态(僵尸进程)

  要谈僵尸状态,我们先思考一下为什么要创建进程?——让进程帮助我们办事,完成某项任务。当然对于完成的结果我们可以关心,也可以不关心。今天我们只谈关心结果。
  我们一般在写main函数时,最后都会写一句return 0;,而这个o表示这个main函数的退出码,可以通过这个退出码来判断进程执行后的结果。
  如果一个进程退出了,立马X状态,立马退出,那么父进程有没有机会拿到退出结果呢?——在Linux中,当进程退出的时候,一般不会立即彻底退出,而是要维持一个状态叫做Z,也叫做僵尸状态。这种状态方便后续父进程或OS读取子进程的退出结果,所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

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

在这里插入图片描述
  当通过kill命令杀死了子进程之后,子进程的状态就变成了Z+,此时它就必须一直被维持下去,直到父进程读取到它的退出结果。
  维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,也就是说,Z状态一直不退出,PCB就要一直被维护。
  那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,从而造成内存泄漏。

二、孤儿进程

  在学习了僵尸进程之后明白了子进程在父进程之前退出,如果父进程一直不获取子进程的退出结果,那么子进程就会进入僵尸状态,子进程也就成了僵尸进程。
  那什么是孤儿进程呢?

2.1 什么是孤儿进程?

  从名字其实就可以感受到,孤儿就是了没有了父母,也就是父进程先退出,而子进程没有了父进程获取它的退出结果,那么子进程就变成了孤儿进程。之所以父进程正常退出没有变成僵尸状态那是因为父进程的父进程是bash父进程退出的时候bash获取到了父进程的退出结果。

2.2 孤儿进程周边

#include <stdio.h>
#include <unistd.h>

int main()
{
	pid_t id=fork();
	if(id==0)
	{
    	//子进程
   		while(1)
    	{
      		printf("i am child,pid:%d,ppid:%d\n",getpid(),getppid());
      		sleep(1);
    	}
  	}
  	else if(id>0)
  	{
    	//父进程 
    	int cnt=10;
    	while(1)
    	{
      		printf("i am father,pid:%d,ppid:%d\n",getpid(),getppid());
      		if(cnt-- <= 0) break;
      		sleep(1);
    	}
  	}
 	return 0;
}

代码功能: 通过fork创建子进程,子进程一直循环打印,父进程在十秒之后退出。

//查看进程的状态信息
while :; do ps axj | head -1 && ps axj | grep myproc | grep myproc | grep -v grep; sleep 1; echo "-----------"; done;

在这里插入图片描述
在这里插入图片描述

  上面两幅图分别是代码刚开始运行所查到的进程信息和在十秒之后的进程状态信息,发现前十秒父子进程都在正常运行,父进程PID为2453,子进程PID为2454。十秒之后,父进程退出,关于PID为2453的进程状态信息已没有,只剩下了子进程的状态信息值得注意的是,此时子进程的父进程PID变成了1,并且它的状态变成了S,为后台进程,需要kill命令将这个进程杀死掉kill -9 2454 或者 killall myproc
  孤儿进程——在父进程退出之后,子进程就会被操作系统自动领养(让1号进程成为新的父进程)。

  为什么操作系统会这样做呢?
  如果不这样做,那么子进程在退出之后会因没有父进程读取它的退出结果而处于一种游离状态,一直占据着相关的内存结构(内存泄露)。


总结

此篇博客首先就是关于进程的几种状态做了解释,主要就是对阻塞和挂起的理解。再者就是明对僵尸状态和僵尸进程的理解,如果子进程先退出而父进程又一直不获取子进程的退出结果,那么子进程就变成了僵尸进程。最后就是对孤儿进程的介绍,明白父进程先退出那么子进程就变成了孤儿进程同时会被1号进程领养。

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

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

相关文章

你知道什么是基于StyleNeRF的conditional GAN模型吗

随着深度学习技术的不断发展&#xff0c;生成对抗网络&#xff08;GAN&#xff09;已经成为了人工智能研究和应用中的重要组成部分。其中&#xff0c;GAN可以被用来生成高质量的图像、视频等内容&#xff0c;这为娱乐产业和数字化制作带来了新的机遇和挑战。本文将介绍一种基于…

numpy 笔记 pad

1 基本介绍 对原本的矩阵进行填充 numpy.pad(array, pad_width, modeconstant, **kwargs) array待填充的矩阵pad_width要填充的位置mode填充方式 2 参数举例 2.0 使用的数据 import numpy as np anp.arange(12).reshape(3,4) aarray([[ 0, 1, 2, 3],[ 4, 5, 6, 7],…

【分布式 】 ELK 企业级日志分析系统

目录 一、ELK概述1.1 ELK简介1.2 为什么要使用ELK1.3 完整日志系统基本特征1.4 ELK的工作原理 二、搭建ELK2.1 ELK Elasticsearch 集群部署&#xff08;在Node1、Node2节点上操作&#xff09;node1 操作node2 同node1操作 2.2 安装 Elasticsearch-head 插件ELK Logstash 部署&a…

作为一个测试工程师,你选择python还是java?

问&#xff1a;“你平时工作中&#xff0c;用java多还是用python多”&#xff1f; 答&#xff1a;“都还可以&#xff0c;根据具体的场景选择不同的语言”。 问&#xff1a;“比如说呢”&#xff1f; 答&#xff1a;“开发自己的测试平台&#xff0c;肯定会选择java&#xf…

Stable Diffusion - ControlNet 插件中扩展局部重绘 InpaintOnly + LaMa 的算法与应用

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131643131 LaMa: https://github.com/advimman/lama Paper: Resolution-robust Large Mask Inpainting with Fourier Convolutions LaMa: Large…

ActiveMQ详细入门教程系列

一、什么是消息中间件 两个系统或两个客户端之间进行消息传送&#xff0c;利用高效可靠的消息传递机制进行平台无关的数据交流&#xff0c;并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型&#xff0c;它可以在分布式环境下扩展进程间的通信。 消息中…

ABeam中国2023社招 | ABeam旗下德硕管理咨询(深圳)招贤纳士

岗位需求 SAP Basis顾问 岗位职责 ■ 参与公司的SAP售前项目&#xff0c;负责Basis相关工作的方案制定 ■ 参与公司既有SAP运维项目&#xff0c;负责Basis相关的课题对应&#xff0c;系统改善等 ■ 负责SAP系统的Basis实施&#xff0c;SAP产品系统安装、升级、迁移、数据归档…

spring-java面向切面拦截器

切面&#xff0c;就是可以在代码执行的时候&#xff0c;在它执行的前面添加一个东西&#xff0c;一般我们用来做登陆拦截器验证以及敏感词的过滤。 他就3个东西&#xff0c;指定切点&#xff08;要执行的代码&#xff09;&#xff0c;before代码执行前面加东西。after代码后加东…

AC220V转负压5V芯片方案-220v转-5vic

AC220V到负压5V的电路转换 问题描述 --------- 如何将AC220V电压转换为负压5V输出&#xff0c;并且在输入电压范围为45V至265V的情况下工作&#xff1f;还要求该电路能够提供可调的电流范围&#xff0c;从100mA至2A。同时&#xff0c;所需的芯片为AH8699&#xff08;在700mA以…

BaGet做了一个Nuget私有服务器,Nginx代理之后还是会请求被代理得地址

Nuget搭建和使用可以参考官网得文档 https://loic-sharma.github.io/BaGet/installation/docker/ 这是我用Nginx代理之后出现得问题&#xff0c;观察请求url和响应回来得配置。配置中得ip地址得url是我被代理得下游地址&#xff0c;所以是无法访问的。 我原本以为是要去server…

怎么扫描二维码看视频?视频转成二维码的技巧

通过扫码来查看视频&#xff0c;是现在很多人都会选择一种视频展现方式&#xff0c;那么怎么制作视频二维码呢&#xff1f;下面给大家分享一个在线二维码生成器&#xff0c;支持多种二维码制作&#xff08;免费在线二维码生成器-二维码在线制作-音视频二维码在线生成工具-机智熊…

手机上有好用的时间管理工具吗?

在日常工作中&#xff0c;时间管理是非常重要的一项技能。这样做不仅可以更好地安排工作计划&#xff0c;还能有效减轻工作压力&#xff0c;减少时间的浪费&#xff0c;从而达到提高工作效率的目的。那么&#xff0c;随着手机的普及和智能化移动应用的不断涌现&#xff0c;我们…

【ARM Coresight 系列文章 4 - ARM Coresight APB-AP 介绍】

文章目录 APB-AP 介绍及使用APB-MuxROM TableROM Table entries bit 分配 APB-AP 介绍及使用 下图是一个 常见的DAP 结构图&#xff0c;其AP使用的是APB-AP。 图 1-1 SWJ-DP 通过监控电路选择外部串行总线SWD或者 外部JTAG&#xff1b;通过 DPACC中的select register 选着AP(…

润和软件HopeStage与易捷行云EasyStack ECF×86云基础设施平台完成产品兼容性互认证

近日&#xff0c;润和软件HopeStage操作系统与北京易捷思达科技发展有限公司&#xff08;以下简称“易捷行云EasyStack”&#xff09;ECF86云基础设施平台完成产品兼容性测试。 测试结果表明&#xff0c;企业级通用操作系统HopeStage V1.0产品与ECF86云基础设施平台可以顺利适…

MF31:VBA_在Excel中加入页码

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

电路分析基础学习(上)第5章

李瀚荪版电分第二版 目录 电容的定义 电容为什么能阻断直流 电容的储能计算公式 电感的定义 电阻与阻抗的区别 电感为什么在直流通路中起着短路的作用 电感的储能计算公式 ----------------------------------------------------------------------------------------…

SSMP整合案例(16) vue java联合实现分页条件查询 终结篇

那么 之后 我们就要处理按条件查询了 简单说 就是我们在分页时 条件也要一起带过去 其实 我们之前写的App组件中 他们已经被定义上了 我们看到App.vue的这个位置 我们用了 formData 其中对应了三个条件 然后 我们在 src的App组件下 找到 getPages 中定义data的部分 然后改成…

idea显示左下角service

展示效果&#xff1a; 解决方案&#xff1a; 1.点击左下角 这个按钮 2.点击蓝色英文&#xff0c;add services 3.找到众多模板中的springboot&#xff0c;点击即可

【C++】手把手教你模拟实现vector

vector模拟实现 前言正式开始三个成员变量无参构造析构push_back[ ]重载pop_backinserterase迭代器失效问题insert迭代器失效erase迭代器失效 深浅拷贝拷贝构造函数赋值运算符重载 n个val构造resizefront和backfrontback 前言 这篇写的是vector的模拟实现。 如果对于vector不…

TI系列——CC2340B1 Uniflash烧录指南

一、物料准备 1.1 硬件准备 1、XDS110-PLUS2.0或者TI LaunchPad&#xff1a; 图 1 XDS110-PLUS2.0中包含&#xff1a; XDS110调试器、USB-A to Type-C-B电缆、具有2x5 2.54mm连接器的10引脚扁平电缆、具有2x5 1.27mm连接器的10引脚扁平电缆、具体请看说明指南文档。 图 2 上…