初识Linux · 进程(4)

news2024/11/15 17:54:49

目录

前言:

进程的状态

直接谈论进程的状态

僵尸进程和孤儿进程

纯理论部分

运行态:

阻塞态:

挂起态:

进程的优先级以及切换问题

切换:

优先级:


前言:

承接上文,进程1到3我们分别介绍了从操作系统层面认识进程,什么是进程,进程的相关属性有哪些,如何创建进程,以及颠覆三观的函数fork,最后介绍了从哪里看进程的部分详细信息,以及深化了一下Linux中一切皆文件的思想->因为进程本质上也是目录!

本文作为进程的收尾工作,要介绍的是进程的状态,什么是僵尸进程,什么是孤儿进程,简单描述进程的调度问题,调度问题会在地址空间详细介绍,以及进程的优先级问题,进程的切换问题等。

更详细的进程介绍会在环境变量以及地址空间介绍完之后,介绍进程控制以及进程替换等,到时候进程才算完结。

好了,废话不多讲,开始今天的第一个话题,进程的状态。


进程的状态

进程的状态分为如下三个部分进行介绍,第一个是直接谈论进程的状态问题,第二个是僵尸进程以及孤儿进程,最后则是进程状态的纯理论,例如挂起态 阻塞态等。

直接谈论进程的状态

我们首先来用ps -xaj引入这个话题:

进程3中创建5个子进程的代码我们作为例子来介绍,我们已经介绍了ppid pid ,状态就是STAT,STAT就是state的简称,state翻译过来就是状态的意思。

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 */
};

Linux的内核源代码中对于状态有一个数组来描述,一共有R S D T t X Z ,共7种状态,其中部分状态我们无法通过代码运行来观察,比如X,dead,表示进程死亡,就是被操作系统回收了,这是一瞬间的事情,我们很难观察。

  5 int main()
  6 {
  7   while(1)
  8   {
  9     printf("Hello world!\n");                                                                   
 10   }
 11 
 12   return 0;
 13 }

我们使用这段代码进入主题,这段代码没有啥问题吧?那么当我们运行起来这段代码的时候,按照常识来说状态应该是R,那来看看:

这边已经开始运行了。

可是为什么我们看运行状态是R和S混在一起,大多数时候还是S居多,难道一个进程可以同时拥有多个状态吗?当然不是的,那么为什么该代码的结果是S和R混一起?我们注释掉printf再看看:

可以发现此时的状态又变成了R,难道因为printf?确实是。

你想,cpu用来执行printf,是不是需要很短很短的时间,甚至超出了我们的想象,但是打印出来是在显示器上打印出来的,所以根据冯诺依曼体系,我们知道,cpu不是和外设直接打交道,那么cpu执行了printf之后,会先将打印结果输入到缓冲区,然后缓冲区刷新到显示器上,那么请问,这个死循环的过程,显示器是否时刻准备就绪呢

当然不是,cpu一瞬间可以给很多很多的printf,显示器打印出来的速度是远远不及cpu运行的速度的,那么把显示器当作是人的话,它指定是会抱怨说:太多了太多了,打印不过来了。

所以,显示器的状态并不是准备就绪的,当我们运气好点,能偶尔看到显示器就绪的状态,此时cpu刚好执行printf,所以状态是R,但是大多数时候显示器可忙不过来。

这就是为什么大多数状态是S的原因,休眠状态,而这种休眠状态还被成为可中断休眠,实际上是一个浅休眠的状态。

这是状态S。

肯定是有细心的同学发现了,这上面的不都是带有+的吗?为什么都说是S啊R啊什么的,是错了吗?当然不是,这是分为进程是前台运行还是后台运行的。

我们可以输入:
./test &

就是代表的指定进程在后台运行,此时,+就不见了,所以,+号有代表的是进程是运行在前台,没有加号代表的是进程运行在后台,此时进程有一个特点就是进程是无法被ctrl + c干掉的,只能通过我们kill指令来杀死:

那么前台和后台的具体参照物是什么这里先不介绍,我们主要还是侧重状态。

那么什么是kil指令呢?kill指令翻译过来也就是杀死的意思,但是并不是完全的杀死,我们可以看看kill指令有哪些子指令:

可以看到不同的数字对应了不同的英文,我们在C语言阶段可以看到的是,一般宏定义就是全英,这里也差不多,不同的数字代表不同的意思而已,我们刚才使用的kill -9,SIGKILL,信号杀死,关于信号后面会另开一篇文章介绍,但是KILL我们是熟悉的,杀死!也就是杀死进程。

从kill我们可以引入第二个点了,T。

T表示的状态是stopped,即暂停,那么kill指令中的-19代表的就是暂停,所以我们看看:

此时就变成了T,至于为什么没有+号了,因为暂停了的进程就变成了后台运行的,这点不用太在意,这就是T,那么暂停了的进程我们想要它跑起来该怎么做呢?

18号指令,SIGCONT,也就是信号继续的意思,CONT就是continue的意思,所以就是信号继续,那么试试:

此时就变化了,所以T的意思就是暂停。

那么什么是t呢?

可以看到源码的意思是trace stop,代表追踪暂停,实际上就是当我们使用断点的时候,代表程序被暂停,所以就是t:

此时通过编译-g,生成二进制的可调试文件,我们开始调试,并打几个断点:

打了断点之后,进程信息里面并没有出现我们想要的东西,因为我们还没有r我们的程序:

r之后进程信息就变成了t,这就是T和t的不同之处,区别不大,总而言之都是暂停的意思。

接着我们介绍D,disk sleep:

这个点我们无法通过演示来观察,所以以文字叙述的方式来了解即可。D状态是Linux中的一个独特的状态,即深度睡眠,比如在内存里面,一个进程要给磁盘写入1GB的数据,那么磁盘写入需要时间吧?写完了还需要给进程说我写好了或者我没有写好,此时进程就等着了,那它也没事干,就睡觉呗,此时操作系统来了,操作系统有特权,即杀死进程或者回收进程的特权,它一来,看到这个进程在睡觉,就气不打一处来,一下子给人回收了,结果磁盘没有写入成功,就导致了1GB的数据丢失,那么老板为了不造成如上的数据丢失的情况,就将进程的状态设置为了D,深度睡眠,深度睡眠的一个特点就是不能被操作,杀死 回收都不可以,必须等磁盘来报告了,状态才改变,这就是D的由来。

好了,状态就介绍完了,接下来是僵尸进程和孤儿进程。

僵尸进程和孤儿进程

僵尸进程和孤儿进程,就可以“顾名思义”了,一个是僵尸,代表杀不死的,那么僵尸怎么形成的呢?即进程运行结束了,但需要维持自己的推出信息,直到父进程来读取,僵尸进程才会被回收,那么我们怎么看僵尸进程呢?

因为僵尸进程是没有父进程回收的子进程,所以我们只需要让父进程一直死循环即可:

  7   pid_t id = fork();
  8   if(id == 0)
  9   {
 10     int cnt = 5;
 11     while(cnt--)
 12     {
 13       printf("I am a child process,pid is %d\n",getpid());
 14       sleep(1);
 15     }                                                                                           
 16   }
 17   else
 18   {
 19     while(1)
 20     {
 21       printf("I am a parent process,pid is %d\n",getppid());
 22       sleep(1);
 23     }
 24   }

当5秒计数过去之后,此时子进程的状态就变成了Z+,也就是僵尸,那么后面跟着的defunct翻译过来就是无效的意思,本质上就是子进程的工作已经完成了,但是要等待父进程完成之后来回收自己,所以它要维护自己的退出信息,退出信息在task_struct里面,那么进程 = task_struct + 自己的代码和数据,变成了僵尸进程之后,代码和数据就用不到了,只需要管结构体就行了,那么如果没有父进程来回来这个僵尸进程,僵尸进程就会一直保持僵尸进程的状态。

那么僵尸进程的危害是什么呢?

内存泄漏!

你要知道,task_struct里面的变量可是很多很多的,根据结构体的内存对齐的规则,自然知道该结构体的大小是很大的,本来操作系统的资源就紧张,万一不小心给父进程杀死了,就没人管它,所以内存就泄露了。

孤儿进程,就是没有父进程的,我们将原来的代码改动一下,使得父进程先退出,子进程一直循环,因为父进程先退出是不用管什么东西的,那么子进程一旦没在父进程之前退出,就会变成了孤儿进程,但是并不是真正意义上的孤儿:

此时,孤儿进程的ppid变成了1,也就是1号进程操作系统来回收,虽说是孤儿进程,但是不能没有人来回收吧?

并且,孤儿进程是不能通过ctrl + c干掉的,只能被kill掉:

纯理论部分

这个章节主要介绍的是运行态,阻塞态,挂起态。

运行态:

对于运行态来说,我们常认为的,进程只要是被cpu执行了,那么就被成为运行态,但是在不同的操作系统的教材上,常常称进程如果在运行队列中也是运行态

head指针后面跟的就是不同的PCB,那么有个问题了,如果一个进程被cpu执行了,是否会等该进程执行完了才会到下一个进程?显然不是,如果是这样,那其他进程也别活了,我就来个死循环,其他进程全部完蛋,所以存在时间片的概念,简单来说就是给定时间,如果该时间内进程没有执行完毕,那这个进程重新到后面排队去,务必要保证其他进程的执行,那么现在埋一个伏笔,进程之间是如何切换的?

这是OS中的基本调度算法,但是Linux中并不是。具体的会在后面介绍。

那么就有了并行和并发的概念,多个进程在cpu这里来回切换以保证多进程的持续推进,叫做并发,一个进程也是可以在多个cpu之间运行的,这种情况叫做并行。

阻塞态:

阻塞?什么是阻塞?等待呗,例如我们调用了scanf函数,但是呢,我们为了作乐,就是不输入,那么操作系统总不能让一个一直等待资源的进程来骚扰自己吧?并且我们知道这种等待资源的进程可以用状态S来表示,即睡眠。

那么OS中是如何操作的呢?

我们首先需要认识一个问题,是只有Cpu才有运行队列吗?或者说只有cpu才有队列吗

当然不是。

如果一个进程等待资源,比如键盘,那么在硬件中,驱动程序也会存在队列,叫做wait_queue。

注:该介绍都是基于Linux的原理部分,但是不代表是Linux的源代码。

wait_queue指向的是什么呢?同进程的链表一样,指向的是device,也就是硬件设备的PCB,源码中可以用define定义数字,表示该PCB是哪种硬件,也可以使用int stat,表示状态,比如定义是否在等待,数据是否输入完成。

那么这个过程,就是叫做阻塞态,即等待资源,有了数据,操作系统就会将对应数据重新拿过来排队,此时进程就从阻塞态变成运行态。

挂起态:

挂起态也是很有意思的,计算机中在磁盘会有一个分区叫做swap,交换的意思,用处是什么?

都知道操作系统的压力一般是比较大的,如果发现内存中的空间不够了,就会从内存中换出一部分资源到磁盘中的swap中分区中,此时进程就变成了挂起态,当然,不是说什么进程都是可以变成挂起态的,肯定OS层面会有一定的分析。

如果内存空间够了,就会从swap分区中拿过来对应的进程,当然,拿过去的只是进程的代码和数据,进程的PCB是不会拿过去的,因为进程的PCB需要记录信息。

swap的空间大小一般都是内存的1.5倍左右,肯定不会太大的,太大了操作系统一依赖这个分区,效率就会下降,本来换入换出就会导致效率下降了,如果更加频繁的换入换出,效率更低了,同算法不同的是,挂起态这里是效率换空间的做法。

挂起态也可以称为阻塞挂起态,因为进程本质上也是没有被调度的


进程的优先级以及切换问题

切换:

进程的切换问题,在运行态中,进程的切换是肯定会有的,那么OS如何保证进程的数据不被丢失呢?

进程的PCB里面可以保存各种信息,那么在时间片到了的时候,task_struct里面会记录cpu中相应的寄存器的值,在下次轮到该进程的时候,就能接着进程的上次运行的结果接着来。

这是因为寄存器本身具有数据存储的能力,寄存器不等于寄存器中的内容是我们要知道的,官方一点来说,这是存储上下文数据,cpu寄存器存储的临时数据,就是进程对应的上下文,我们不必担心寄存器不够,寄存器可多着呢。

优先级:

优先级相信不用过多介绍同学们也知道为什么存在优先级,优先级如何判断。

优先级的存在就是为了公平,比如我们去排队,总得有个先后顺序吧,这就是优先级的作用,为了保证公平,那么优先级VS权限呢?

一个是已经拥有访问的权限,不过是时间问题,一个是不知道能不能访问,所以它两之间的比较就不多说了。

在PCB里面,总会有字段是用来描述优先级的,比如int prio,这个数字越小,代表优先级越大,具体参考进程1,代表的是操作系统,这优先级能不大嘛?

查看优先级我们需要top一下,ps -al也可以的,top之后,打开了一个类似于任务管理器的东西,其中PR代表的就是优先级的数字,Ni代表的是nice值,我们可以通过nice值来修改优先级的大小,当我们是普通用户的时候,优先级修改第二次就会报警告,除非我们是root用户才会让我们修改。

所有优先级 = PR + NI,这里的NI是有一定的取值范围的,是[-20,19],如果我们修改NI为100,只会取19,不会超出去。

其中修改nice值也很简单,只需要nice或者是renice指令,因为很简单这里不介绍。

我们一般可以通过top进入到了任务管理器,然后按r,输入对应的pid,回车再输入对应的nice值即可。

这就是优先级我们要注意的内容。


感谢阅读!

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

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

相关文章

CPU 和 GPU:为什么GPU更适合深度学习?

目录 什么是 CPU ? 什么是 GPU ? GPU vs CPU 差异性对比分析 GPU 是如何工作的 ? GPU 与 CPU 是如何协同工作的 ? GPU vs CPU 类型解析 GPU 应用于深度学习 什么是 CPU ? CPU(中央处理器)…

二叉树链式结构遍历(指针、递归)

当二叉树不使用数组实现,而是使用链式结构,用指针将节点相连时, 二叉树便会衍生出很多问题,如前序遍历、中序遍历、后序遍历、层序遍历。 下面我将用递归的方法完成二叉树的遍历。 但在这之前,我们还得构造链式结构…

深入解析 Apache Ranger

一. 概述 1.什么是 Apache Ranger? Apache Ranger 是一个为大数据平台提供集中化安全管理的开源框架,专门用于确保 Hadoop 生态系统中的数据安全。Ranger 通过提供细粒度的访问控制和监控,帮助组织实现对数据的全面安全管理,确保…

第四天旅游线路预览——从换乘中心到观鱼台

第四天:从贾登峪到喀纳斯风景区入口,晚上住宿贾登峪; 换乘中心有4 路车,喀纳斯②号车,去观鱼台,路程时长约20分钟; 将上面的的行程安排进行动态展示,具体步骤见”Google earth stu…

7.Jmeter数据驱动(csv数据文件设置)+Jmeter数据库操作

一、Jmeter数据驱动 因为:工作中,有些公司一个接口就是一个吻用例,另外一些公司一个接口有几十个用例,就需要用到数据驱动。 特别注意:断言,如果有共同字段,那么就用json断言,如果没…

文档管理系统Mayan EDMS的安装

今天台风 “贝碧嘉” 在上海登陆,这名字起的那叫一个绝,听起来像是 卑鄙家, 说的就是这台风 卑鄙到家了 什么是 EDMS? EDMS 代表电子文档管理系统,它是 DMS 的更现代版本。DMS 是文档管理系统。一种用于存储、排序和分…

二叉树的前中后序遍历(非递归迭代实现)

1.二叉树的前序遍历 1.1 思路分析 前序遍历的顺序是根-左子树-右子树,所以首先从根节点开始,顺着访问左子树:1、2、4。此时,还剩下节点1、节点2、节点3的右子树没有访问。后面倒着访问节点1、2、4的右子树就行了。所以非递归的前…

解决跨境电商平台账号无法访问的常见问题

跨境电商的迅猛发展,越来越多的卖家选择在全球各大电商平台如亚马逊、eBay等进行商品销售。然而,在实际运营过程中,卖家经常会遇到账号无法访问、应用打不开等问题,导致业务受阻。本文将针对这些问题进行详细分析,并提…

【医疗影像】THE BEER-LAMBERT LAW

吸光度 A l o g 10 ( I 0 I ) A log_{10}(\frac{I_0} I) Alog10​(II0​​) 如果您了解光谱仪如何工作,您就会知道它使一系列波长的光穿过物质溶液(样品cell),同样地,也会穿过溶剂(参考cell) …

信息安全工程师(5)域名与域名解析

一、域名 1. 定义与功能 域名(Domain Name)是互联网上用于标识网站或服务器地址的名称,由一串由点分隔的字符组成,如“example.com”。域名的主要功能是提供一种便于记忆和输入的地址形式,以代替难以记忆的IP地址。域名…

【Axure原型】B端系统登录注册页设计成这样,就不用跟小孩一桌了

前言 在B端后台中,登录注册页这个东西,因为感觉很简单,所以经常不被产品经理们重视。但是登录注册页作为一个后台系统的门面,直接影响用户第一印象,又是非常重要的存在。 登录注册页的价值 B端系统登录注册页是用户…

浸没边界法空间精度相关的论文的阅读笔记

Convergence proof of the velocity field for a stokes flow immersed boundary method https://doi.org/10.1002/cpa.20233 研究对象的选取 他这里为什么能够选取一个周期性边界的流场啊?为什么不是狄利克雷边界或者诺伊曼边界? 方形流场的边界值 …

【Linux】精通GDB:打造你的Linux调试超能力

🌈 个人主页:Zfox_ 🔥 系列专栏:C从入门到精通 目录 一: 🔥 什么是GDB / CGDB 二: 🔥 CGDB的安装 🍊 Linux-centos 三: 🔥 cgdb的使用背景 &#…

vmware,centos8(虚拟机) 的安装

安装vmware 点击下方网址 虚拟机安装地址https://www1.msc23.cn/vm/?bd_vid8829610582362807097选择VMware17 打开文件所在地,双击安装 同意条款 选择安装位置 不将VMware配置到环境变量path 不检查更新,不加入客户体验 创建桌面快捷方式 开始安装 安装完成…

47.面向对象综合训练-汽车

//题目需求:定义数组存储3个汽车对象 //汽车的属性:品牌,价格,颜色 //创建三个汽车对象,数据通过键盘录入而来,并把数据存入到数组当中 1.标准的JavaBean类 public class Car {private String brand;//品…

基于yolov8的谢韦尔钢材缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的谢韦尔钢材缺陷检测系统,充分利用了YOLOv8算法的高效性和准确性,为钢材表面缺陷检测提供了先进的解决方案。该系统通过深度学习技术,特别是YOLOv8这一实时目标检测算法,能够快速、准确地识别钢材表面的…

【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:Linux “ 登神长阶 ” 🤡往期回顾🤡:暂无 🌹🌹期待您的关注 🌹🌹 ❀Linux进程 📒1. 获取输入&…

《Nginx核心技术》第17章:使用自签CA配置HTTPS加密反向代理访问

作者:冰河 星球:http://m6z.cn/6aeFbs 博客:https://binghe.gitcode.host 文章汇总:https://binghe.gitcode.host/md/all/all.html 星球项目地址:https://binghe.gitcode.host/md/zsxq/introduce.html 沉淀&#xff0c…

事务方法中存在远程调用

1. 背景 在实现下单的方法中,首先通过远程调用查询了地址簿信息和服务信息等,然后将这些信息和前端传入的预约时间、服务项等信息封装到order实体类,然后调用Mybatis-Plus提供的ServiceImpl类的save方法,向数据库表中插入数据。 …

基于51单片机的220V交流数字电流表proteus仿真

地址:https://pan.baidu.com/s/1QmpPLvDTuW7QG7P-JCLPPg 提取码:1234 仿真图: 芯片/模块的特点: AT89C52/AT89C51简介: AT89C52/AT89C51是一款经典的8位单片机,是意法半导体(STMicroelectron…