Linux —— 进程概念超详解!

目录

1.什么是进程/p>

2.进程的状态

3.Linux是怎么做的

4.Linux的进程管理

5.僵尸进程

6.孤儿进程

7.进程优先级

 8.进程的四个重要概念

9.环境变量

1.什么是进程/h1>

即使我们不会编程,我们依然可以很容易的理解什么是进程。如果我们是windos操作系统,我们可以打开任务管理器,点击进程一行可以观察进程:

Linux —— 进程概念超详解!

 我们可以发现,进程占用了cpu与内存的空间,并且我们知道:cpu有强大的指令集,可以处理很多很多指令,但是cpu只能被动的接收别人的指令,然后处理别人的数据。这些指令和数据从何而来实上在我们双击运行一个可执行文件时,这个可执行文件就会被加载到内存当中去,此时,内存当中被加载的可执行文件就是进程。此时在操作系统的角度看来,二进制指令冗余并且繁杂,非常难以管理,所以操作系统会生成一个内核结构体——PCB结构体。我们知道C语言中的结构体是用来描述某一时间的属性的,那么进程也是一个事件,进程也有属性一个进程对应一个PCB结构体,操作系统只需通过此结构体就能找到对应的二进制指令。那么当有多个程序被加载到内存时,就会生成多个PCB结构体,操作系统非常聪明,把这些结构体通过某种数据结构组织起来,这样操作系统可以非常方便的访问各个PCB结构体。

Linux —— 进程概念超详解!

 我们可以用这么一句话来描述操作系统有多厉害:操作系统既管做饭,又管喂饭,还负责洗碗……刚才我们也说了,cpu只能被动的接收指令,这些指令就相当于饭,这些饭通过操作系统给cpu“喂”过去,然后cpu只需要负责“吃”。刚才也说了,操作系统是通过PCB找到原生代码的,那么给cpu送过去的也是PCB结构体,如果进程很多,那操作系统是等cpu执行完一个进程后再喂一个PCB给cpu吗然不是!cpu有一个运行队列,操作系统把PCB结构体放在cpu的运行队列上,当PCB结构体到cpu的门口了,操作系统才会通过PCB找到原生代码,再将这些代码喂给cpu。

Linux —— 进程概念超详解!

2.进程的状态

有了PCB结构体和数据结构,很大程度上能够提升管理进程的效率,但远远不够。我们需要将进程进行分类,映射到现实生活当中也是如此:例如图书馆,图书管理员为了方便管理图书,会为每本书打上简介,并通过把书放在书架上的方式来存储图书……那么进程如何分类过状态。每个进程都有自己的状态,这些状态能够告诉操作系统我正在干什么、我将要干什么,也就是说,进程的多种状态,本质都是为了满足未来的某种使用场景

如果我们熟悉windows,我们可以大体知道进程的一些状态:

Linux —— 进程概念超详解!

 那么我们接下来着重介绍的便是三种状态:

  • 运行状态
  • 阻塞状态
  • 挂起状态

运行状态:按照字面理解的话,那就是cpu正在处理的进程。这句话本没有错,但我认为运行状态是指:进程的PCB结构体正在cpu的运行队列上排队

阻塞状态:每种硬件都有一个等待队列,那么进程的PCB结构体被操作系统放在这个等待队列时,这个进程就处于阻塞状态,通常也称等待状态。

挂起状态:内存满负荷时,又要增加新的进程显然是不行的。所以操作系统会观察内存中的哪些进程没有被放在任何一个队列里面(在内存里面啥也不干),找到以后就把此进程的代码和数据短期内置换到磁盘上,仅保留此进程的PCB。腾出的这一块空间供新的进程使用。这个动作,就叫做挂起,被挂起的进程,状态处于挂起状态。

实际上,不同的进程状态,其本质就是处于不同的队列

3.Linux是怎么做的

上面介绍的是非常枯燥的操作系统的理论知识,现在我们把目光聚集在Linux这款操作系统上,观察Linux是如何描述、管理进程的。

那么在Linux内核里,是如何描述进程状态的呢/p>

源码关于进程状态的描述是一个指针数组,其中各个字母的含义为:

  • R (Running):该进程正在运行中。
  • S (Sleep):该进程目前正在睡眠状态,但可以被唤醒。
  • D :不可被唤醒的睡眠状态,通常这个进程可能在等待I/O的情况。
  • T :停止状态,发送一个暂停信号给进程,进程就暂停了。
  • t :追踪停止状态,通常在断点调试时,进程处于此状态。
  • X :死亡状态,这个状态是用来告诉操作系统的,所以我们观察不到此状态。
  • Z (Zombie):僵尸状态,进程已经死亡,但是却无法被删除至内存外。

那么想要在Linux环境下观察进程的状态,我们可用的指令有两个:

ps aux 或者 ps ajx                <==查看系统所有的进程

ps -lA                <==也是能够查看系统的所有进程

 我们截取一段截图,来观察Linux的具体描述:

Linux —— 进程概念超详解!

可以看见,指令的下一行就是列表参数,其中的STAT就是咱们要找的进程状态。此时我们的指令正处于运行状态(观察的进程状态是当时发生的动作,所以是运行状态)。

我们也应该注意到PID。PID是什么span style=”color:#fe2c24;”>PID是进程的唯一标识符,这个标识符是操作系统给的,操作系统把PID放在进程的PCB中。

现在,我们学会了最基本的查看进程。此时我们需要结合我们自己写的程序来进一步熟悉进程和Linux。

我们输入以下代码并比编译生成一个名为test的可执行文件:

然后运行此程序,观察此进程的状态。为了提高效率,我们的指令可以升级为:

ps ajx | head -1 && ps ajx | grep test

Linux —— 进程概念超详解!可以看到,我们自己的程序处于运行状态啦!并且拥有自己的PID。但是我们看到,还有一个叫做PPID的东西,这个PPID是我们的进程创建时所依赖的进程的标识符。 我们再通过一条指令去观察这个PPID是谁的标识符:

ps ajx | head -1 && ps ajx | grep 4034                <==这个PPID不唯一

 

Linux —— 进程概念超详解!

可以看到,这个4034的PID是bash进程的标识符,这个bash进程也有一个PPID。bash是什么们可以理解为命令输入行。这个命令行输入也就是让我们输入指令的地方,是不是我们一打开Linux就有了以,bash是当我们登录Linux操作系统时,操作系统会自动获取一个bash的shell(与以前介绍的shell原理一样,我们并不能直接操作bash),那么此时bash就被加载到内存里面去啦!所以bash是登录操作系统时自动加载到内存里的进程。在这个bash进程的基础上,我们才可以输入指令去让Linux完成某些操作,所以,指令的运行是依赖bash的。那么这种依赖关系,我们称作[父子进程],指令需要依赖bash,所以指令称为[子进程],bash称为[父进程]。同理,我们在命令输入行输入[ls]这个指令,ls是子进程,bash是父进程。

现在,我们需要知道Linux是如何描述阻塞状态的。我们复习一下阻塞状态的定义:进程的PCB被加载到硬件的等待队列当中。那么C语言有什么函数能够访问硬件rintf嘛!在原有的代码基础上修改程序:

运行代码,再输入指令观察进程的状态: 

Linux —— 进程概念超详解!

这里绝对有人有疑问,我们明明运行程序了啊!为什么是S状态!而不是R状态!实际上这两种状态都有!我们要清楚一个概念,硬件的处理速度有cpu快吗然没有!cpu处理指令的单位时间是纳秒!所以可以打一个比方,执行一个程序要的时间为100秒,那么这个进程在硬件的等待队列当中就可能待了99.99秒,所以从概率上来看,我们的test进程是S状态,就理解啦!所以呢,Linux中,使用S状态描述阻塞状态,挂起也用S状态描述。绝对不是因为懒,Linus才这么设计的,而是因为任何一个操作系统的使用者,都不会关心这些问题。

还有一个D状态,我在这里浅浅的普及一下:D状态是平时使用时几乎不会碰到的东西。D状态是一个深度睡眠的状态,通常发生在与硬件的交互中。比如,当我们写的代码,要访问硬件,要交换的数据又臭又长多到无边无际,再加上硬件的处理速度很慢,这就会导致其他的进程要访问这个硬件,必须在等待前一个进程与硬件交互完毕。但是这毕竟是个合法状态,我们没有办法使用kill指令杀掉这个进程,因为如果杀掉这个进程,硬件就非常尴尬了(怎么深入了解还没到一半人就没了,在操作系统的角度来看,这样的交互是合法的,因为我们的进程设计就是要先与硬件交互再被cpu执行啊,有毛病吗以操作系统也管不了。那么一旦出现了D状态的进程,就相当于这个操作系统无法正常执行任务了。解决的办法只有两种:一是等这个进程处理完,而是掉电重启。

4.Linux的进程管理

进程之间是可以互相控制的。最典型的例子就是打开某个软件、关闭某个软件,我们无法直接隔着屏幕大喊一声:”我要关掉你!“这个软件就会关闭掉,而是需要通过一个进程给这个软件(也是进程)一个关闭信号,这样就形成了进程的互相控制。

刚才提到了一个操作,叫做使用kill指令杀掉进程。实际上在以前,电脑还很贵的时候,那时候玩游戏都得看脸,经常各种程序死机,然后都会统一的调出任务管理器强制结束进程:

Linux —— 进程概念超详解!

那么Linux也有这样的操作,就是使用kill指令:

kill -9 [PID]                <==通过PID去杀掉进程

[-9]是什么是一个信号,这个信号代表的意思为强制中断一个进程的执行,也就是从内存当中删除这个进程。那么我在这里列举几个常用的控制信号,供大家作为参考: 

代号 名称 内容
9 SIGKILL 强制删除一个进程
18 SIGCONT 继续一个进程的运行
19 SIGSTOP 暂停一个进程的运行

对于命令输入的格式,我们以杀掉某个进程举例,还可以这么写:

kill -SIGKILL [PID]                <==与上面的效果等价

 那么我们还是以这段代码为例,演示这三个信号的使用以及观察不同信号下的进程状态:

Linux —— 进程概念超详解!

 可以看到,随着不同的信号输入,进程的状态也随之变化。上图中我们观察到了前面所提及的进程的T状态,在这里得到了验证。不过有很多读者可能比较奇怪,为什么某些进程的状态会有一个’+’呢span style=”color:#fe2c24;”>有无’+’仅仅是为了区分是前台还是后台,这些信息是告诉操作系统的,而不是告诉用户的。

那么我们可以向进程发送多少种信号们可以通过这个指令来查看一共有多少种信号:

kill -l                <==l是小写的L,查看所有的信号

我们还可以发现一个非常麻烦的东西:因为[kill]这个指令是专门针对进程的PID的,所以每次使用[kill]命令时都得配合[ps]命令来查看要操作的进程的PID。那有没有什么办法,我们直接操作进程的名称而不是它的PID呢—[killall]命令。我们使用[killall]命令重复一遍上面的操作:

是不是方便许多呢/p>

5.僵尸进程

没有走错片场!朋友们!是的,没有听错,就是僵尸进程。僵尸是什么体嘛!谁的尸体程的嘛!

当有一个进程要退出的时候,它是直接原地消失、释放空间的吗然不是了,如果真是这样那么对于操作系统来说就非常奇怪了——刚才还好好的,回个头怎么人不见了span style=”color:#fe2c24;”>当进程退出的时候,它是不会立即释放空间的,它的PCB会保存一段时间让父进程或者操作系统读取,让父进程或操作系统知道这个进程即将退出了,然后父进程或者操作系统释放掉进程占用的资源和空间一般情况下,清理进程资源空间的操作都是父进程

我举一个形象的例子:有一天进程A吃完饭没事干在内存里面溜达,突然看到进程B被一道指令给咔嚓倒在地上了,完了没呼吸了。那么进程A就只看见了内存里面躺着一具进程B的尸体,尸体不能凭空消失啊!此时进程A又看到了一个贼大的进程,这个进程脸上写着五个大字——B的父进程,然后就看见这个父进程对着B进程的尸体一顿处理,然后尸体就消失了,内存里面再也看不到B进程了。

那么僵尸进程,指的是什么呢是进程退出时,依然会在内存里面待一段时间,如果父进程没有能力将此进程完整地释放掉,造成这个进程一直在内存里面,此时这个进程就是僵尸进程

我们可以写一段程序,来探究僵尸进程是怎样产生的:

让此程序运行,kill掉子进程,观察子进程的状态:

Linux —— 进程概念超详解!

可以看到我们的子进程已经是僵尸进程了,再重复一遍,僵尸进程产生的原因是:当进程要退出时,其占用的资源空间会在内存中保留一段时间,这段时间是专门让它的父进程来处理的;但是如果父进程一直没来处理,导致进程一直在内存当中,这个进程就是僵尸进程了

为什么上图我们能看到僵尸进程呢ill其他的进程而不会看到僵尸进程呢/strong>这是因为我们代码的设计问题,我们仔细观察代码,我们设计父进程处理子进程”尸体”的行为了吗以产生僵尸进程的原因通常有三个:一是操作系统不稳定;二是代码写的不好;三是用户的不良操作习惯导致的

僵尸进程通常是无法再进行管理的,所以我们不能直接杀kill掉它,而是交给操作系统来处理这个进程。如果连操作系统都无法解决掉这个僵尸进程,那就只能通过其他手段了,例如重装系统。僵尸进程的危害也是非常大的,它会导致内存泄漏

6.孤儿进程

很好理解吧是没有父进程的子进程。什么情况下才会发生span style=”color:#fe2c24;”>当父进程衍生出一个或多个子进程后,父进程先退

来源:龙兆万

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年10月15日
下一篇 2022年10月15日

相关推荐