一探Linux下的七大进程状态
文章目录
- 一、前言
- 二、操作系统学科下的进程状态
- 1、运行状态
- 2、阻塞状态
- 3、挂起状态
- 三、Linux下的7种进程状态
- 1、运行状态R
- 2、浅度睡眠状态S
- 3、深度睡眠状态D
- 一场有趣的官司
- 4、停止状态T
- 5、进程跟踪状态t
- 6、死亡状态X
- 7、僵死状态Z —— 两个特殊进程
- ① 僵尸进程
- ② 孤儿进程
- 四、总结与提炼
一、前言
Hello,大家好,本文我们所要介绍的是有关Linux下的进程状态
- 在上一文中,我们重点介绍了有关 Linux下进程的基本概念,了解了什么是进程、怎么去描述并组织进程、创建一个进程。
- 在本文中,我们将先通过了解操作系统学科下的进程状态,对进程的状态有一个基本的概念,然后呢再去学习Linux下的7种进程状态,学习这PCB结构体中的第二个成员变量
task_ struct内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
二、操作系统学科下的进程状态
- 对于进程而言呢,它是操作系统中的概念,如果有学习过《操作系统》这门学科的话应该可以很清楚对于进程而言的话是存在着许许多多的状态,如果一开始刚刚接触的小伙伴一定会感觉这么多状态要怎么区分呀😵
其实那么多的状态,真正主要的也就那么几个,所以接下去我会中重点讲解以下几种进程的状态
1、运行状态
首先我们要谈到的是【运行状态】,这个状态是最普遍的
- 首先对于一个进程而言,我们知道它是由 内核数据结构 + 所对应的代码和数据 所组成的,所以当系统中存在多个进程的时候就势必会存在多个结构体;当然我们需要将这些进程给链接组织起来
- 那么这些进程就相当于是在处在一个运行队列中,我们如果要找到这个队列中的某个进程的话,只需要找到这个进程的头部即可,那我们就可以对应地去调度某个进程,把这个进程所对应的代码和数据放到CPU上去执行
💬 因为每个进程是需要去竞争CPU资源的,但是呢CPU不可能同时给这么多进程分配资源
- 所以每一个CPU都会去维护一个运行队列,里面的队头指针
head
所指向就是第一个进程所对应的【task_struct】,队尾指针tail
所指向就是最后一个所对应的【task_struct】。所以我们要运行某一个进程只需要将 head 所指向的那个进程放到CPU上去运行即可
- 但是CPU无法直接去找到某个需要调度的进程来运行,此时呢就需要一种东西叫做【调度器】,CPU通过找到运行队列,运行队列找到调度器,然后调度器再去一一调度所需要运行的进程,那么此时所被调度的、处于运行队列里的这些进程所处的状态我们称之为 运行状态R
提问:一个进程只要把自己放到CPU上开始运行了,是不是一直要到执行完毕,才把自己放下来?
- 不是,每一个进程都有一个叫做:时间片的概念! 其时间大概是在10 ms左右。所以并不是一个进程一直在执行,而是这多个进程在一个时间段内所有代码都会被执行 —— 这就叫做【并发执行】
- 所以呢这就一定会存在大量的进程被CPU放上去、拿下来的动作 —— 这就叫做【进程切换】
💬 所以呢我们不要拿自己的时间感受去衡量CPU,其运行一遍速度是非常快的,你根本感受不到这种进程切换的效果
2、阻塞状态
在介绍完【运行状态】后,我们再来讲讲【阻塞状态】
- 之前我们在讲 操作系统基本概念 的时候,有说到过操作系统要去管理底层的硬件的话需要进行【先描述,再组织】的动作,那就是将这一个个生冷的硬件抽象成为结构体类型,内部的
type
代表这是一个什么类型的硬件、status
代表这个硬件当前在操作系统中所处的状态是什么样的,wait_queue
代表的则是等待队列的指针
- 那我们继续去谈起操作系统中的【进程】,现在呢有一个进程想要去读取键盘中的数据,那么它就需要与我们刚才所描述的结构体相互勾连起来,因为操作系统根据这个结构体顺着体系结构去访问到最底层的硬件需要实现,所以这个进程便需要一直处于等待状态,我们可以将其链入【等待队列】中,即我们刚才所讲的
wait_queue
💬 那有同学可可能会疑惑这为什么叫做【等待队列】呢?明显只有一个进程鸭🦆
- 一个进程当然不能算是队列,若此时又有一个进程也要来读取键盘中数据的话,我们就需要将其链入到这个等待队列中。此时这两个进程所处的状态即为 阻塞状态
- 那么当键盘里一旦有数据了,我们只需要把这个进程放到我们在上面所讲的【运行队列】中即可,那么CPU在调度的时候就可以自动地到底层设备里去读取了;如果当键盘里没有输入的时候,最终我们的进程就只能在每一个各自的等待队列里去等待,把这种处于等待队列的进程称之为 阻塞状态
3、挂起状态
最后我们再来讲讲一种状态叫做【挂起状态】
- 对于操作系统而言,我们知道它要为当前的所有进程分配内存资源,在上面我只画了一个等待队列,而且只有两个进程,若是一个等待队列中存在多个进程,并且通过有多个等待队列在排队的话,操作系统中的内存资源就会出现【消耗殆尽】的问题
- 那么操作系统就得保证在正常情况下省出来内存资源
要怎么去省呢?
- 只要一个进程没有被调度(运行),那么当前这个进程的 代码和数据 就是空闲的、没有被使用的。于是操作系统就想办法把这个进程内核的PCB给保留,然后将其 代码和数据 重新交换到外设当中【换出】,那么此时只有一个PCB在这里排队,我们把上面这种状态就称之为 挂起状态
- 当这个进程就绪了,把这个进程放到运行队列时,此时再考虑把 代码和数据 重新放回进程中【换入】
💬 那有的同学说:为什么要这样去做呢?这样做有什么意义?
- 答:当一个进程所对应的代码和数据交换到外设上时,操作系统内部就腾出了相应的空间,此时OS就可以将这块多出来的空间重新给到其他需要的进程使用,从而达到了循环利用系统资源的目的,节省了系统开销。
三、Linux下的7种进程状态
在介绍完操作系统学科下的三种最主要进程状态后,我们对进程的状态有了基本的概念,接下去就让我们正式地来学习一下Linux系统下7种进程状态
先来小结并回顾一下上面所学:
- 如果当前是【运行状态】,那么接下来就需要被调度运行
- 如果当前是【阻塞状态】,那就等条件就绪,等设备准备好就把当前进程投递到运行队列里,然后再被CPU调度运行
- 如果当前是【挂起状态】,要做的就是把当时换出的代码和数据重新换入,然后再把所对应的进程列入到运行队列中
以下就是关于进程的所有状态
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 */
};
1、运行状态R
首先我们要来聊的是【运行状态R】
- 来看下下面的这段代码,是一个死循环去
printf
打印循环语句
1 #include <stdio.h>2 #include <unistd.h>3 4 int main(void)5 {6 while(1); 7 {8 printf("hello bit\n");9 }10 11 return 0;12 }
- 然后我们将下面的代码给运行起来,观察这个进程的状态时,看到其显示为
S+
,不过呢读者想看到的应该是R
才对
- 接下去呢,我们把代码修改一下再来看看,不使用
printf
打印语句了,而且直接使用一个while(1)
去做循环
1 #include <stdio.h>2 #include <unistd.h>3 4 int main(void)5 {6 while(1); 7 //{8 // printf("hello bit\n");9 //}10 11 return 0;12 }
- 然后我们看到,此时再运行起来时这个进程的状态就改变了,变成了
R+
,这才是我们想要的【进程状态】
💬 那有读者就要问了:为什么把printf
打印语句给去掉之后就变成这样了呢?
- 原因就在于
printf
打印语句它是属于IO流的一种,第一次因为是循环的缘故,它一直在等IO设备就绪,所以其进程状态就一直为S+
,对应的即是在操作系统中的【阻塞状态】;但是当我们去掉printf
这种IO流之后呢,它就是在纯纯运行,没有IO,那也就变成了R
状态
这里再补充说明一下这个
S
和R
后面的+
- 这里的
R+
代表的就是这个进程是在前台运行的,所以我们在输入任何指令后不会对其造成 任何的影响
- 那若是我们不以正常的方式去启动这个进程的话,其进程的状态就会不一样了,可以看到我在
./myproc
的后面加上了一个&
;那么其状态变成了R
,此代表的意思就是这个进程它是运行在了【后台】的
2、浅度睡眠状态S
接下去我们再来介绍一下Linux下的睡眠状态S
- 下面我们要运行的代码是每隔一秒打印一句内容
1 #include <stdio.h>2 #include <unistd.h>3 4 int main(void)5 {6 while(1)7 {8 sleep(1); 9 printf("hello bit\n");10 }11 12 return 0;13 }
- 运行起来后可以看到这个进程所处的状态即为
S+
睡眠状态
💬 那有同学就很疑惑,这个进程不是在运行吗?为什么不是R
状态呢?
- 我们可以通过动图来观察看看,每一次去查看这个进程的状态时,都是处于
S+
这个睡眠状态。其实呢,这和外设与CPU的速度之差是有关系的,CPU那个运行速度是极其快速的 - 虽然我们肉眼可见右侧的进程是一直在每隔一秒运行,但是呢在我们查进程状态的时候看到的却只能是
S+
;因为有99.99%
的时间都在进行等待,而只有0.01%
的时间在运行,它们之间简直可以说是数量级别的差别
- 不过呢,上面这样还无法看出一个进程是否真正地处于睡眠状态,我们再通过一段代码来看看
1 #include <stdio.h>2 #include <unistd.h>3 4 int main(void)5 {6 int a = 0;7 printf("Enter# ");8 scanf("%d", &a);9 10 printf("echo : %d\n", a);11 return 0; 12 }
- 将该进程运行起来我们可以看到其是出于
S+
的状态,因为【shell】此时正在等待用户的输入,这个就对应到了我们上面所讲到的 阻塞状态
- 不仅如此,像我们一直在使用的
bash
,也可以算是一种【阻塞状态】,一直等待着我们去输入命令行,一旦有了的话它就进行读取
3、深度睡眠状态D
除了【浅度睡眠】之外呢,还有一种叫做【深度睡眠】,它们俩呢,都是 阻塞状态
- 对于浅度睡眠来说,之所以称为 “浅度”,是有原因的:也就是处于这种状态的进程容易被唤醒。例如说我们在上面所讲到的这个处于阻塞状态的进程,我们使用
kill -9 8664
向这个进程发送【9号信号】,那么这个进程就被杀死了,你也可以认为被唤醒了
好,接下去呢我就通过一个故事来描述一下这个【深度睡眠】到底是怎样一种状态
- 现在在操作系统里有一个进程,它呢想要把数据写到磁盘上去,磁盘这个时候听到了它的请求,就开始写数据,不过我们知道外设尤其是像磁盘这种设备的速度都是比较慢的,所以呢这个时候进程就会一直地等待下去
- 但是呢,光这么无休止地等待下去可不行。因为当前这个时候操作系统中的已经有非常多的进程了,那么OS它看不下去了,路过的时候看到当前的这个进程正处于等待状态,也不运行,于是二话不说把它终止了
💬 所以呢,同学们,我们要明白这么一个道理:操作系统认为当前系统能行的话就直接让用户去用就可以了,如果扛不住了就置换页表,实在不行了就去会杀进程🔪
- 那么当这个进程被杀掉的时候,磁盘说:我在写数据的时候出了问题。它这个时候就回去找那个调度它的进程,但是发现这个进程没了???所以磁盘就特别尴尬:我要怎么办呢?写好的这个数据该不该还放在这里【一般的设备都会选择丢失】
- 如果这份数据是普通的那还好,但如果其为银行当中那种非常重要的数据呢?该怎么办?
一场有趣的官司
那因为随着这份重要数据的丢失呢,就引发了一场官司🔨
- 到了法庭上,法官开始问话,审讯【操作系统】、【进程】、【磁盘】这三个嫌疑人,读者认为是谁的问题的?锅在谁呢?
以下呢,是三个人的辩词
💬 【操作系统】:
- 请问有没有赋予我管理它软硬件资源的权利?请问我杀掉进程是不是在特别极端的情况下杀掉的?请问我有没有履行我操作系统的职责?我的职责是保证系统不挂?数据丢失和我有什么关系?
- 我就是在做我操作系统该做的事,如果你判我有罪了,那么请问我下次再遇到类似的情况时,到底我还做不做,我如果不杀的话,最终导致操作系统挂掉:① 数据该丢还是得丢;② 可能还会影响其他进程。这些责任谁来承担呢?
操作系统听了这番说辞后心里想了想,确实是。于是呢这个时候便把茅头指向了丢掉数据的磁盘✒ 发问道:你为什么要丢数据呢?
💬 【磁盘】:
- 法官大人啊,这你不能怪我╮(╯▽╰)╭ 在这件事情上我就是个跑腿的,人家让我干啥就干啥,我在写入的时候就已经告诉了对方我会失败,我让他去等的,我要给它结果的,我的工作模式向来都是这样,其他磁盘也是这样
- 如果你认为我有罪的话,是不是其他的磁盘也有问题呢?我丢失也是有原因的,其他进程也要叫我写入数据,那我也去写了,并且汇报回去了,那其他磁盘怎么没出问题呢?
操作系统一听:诶,这货说的好像确实没什么问题。那就就把视角转向了受害人即【进程】这一方。反正操作系统没错、磁盘没错,那就是你的问题喽!
💬 【进程】:
- 法官,我可是受害人啊/(ㄒoㄒ)/~~ 我就是静静地坐在那里,“人在家中坐,锅从天上来呀”。我是被杀掉的,我怎么能有错呢?
那这个时候法官一想,它们三个说的似乎都挺有道理,难道是我错了吗?
- 其实上面三者都没错,操作系统履行了它的职责,在关键时刻杀掉了进程;而磁盘呢则是起到了它本能的义务,丢失了没人要的数据;进程呢则因为在等候期间无意中被操作系统给杀掉了
- 当这个进程在等磁盘写入数据这么关键的时候,应该要让其受到保护,不可以被任何人给杀掉才对
那我们在上面有提到过处于【浅度睡眠】的进程,是可以被
kill
掉的,那么我们就要让这个进程的状态变为【深度睡眠】才对,即[D]
- 那既然这个进程的状态被设置为
D
后,当它在等待这个磁盘写入的时候,操作系统路过,看到这个进程没有跑起来空等着,于是在想把他杀掉的时候就被这块 《秒死金牌》给吓到了😮
- 那么这个进程就不会被杀死了,当磁盘写完数据后告知进程,那么它就可以将自己放入【运行队列】里去运行,此时它的状态就变成
R
了
💬 那这个时候就又有同学问了:D状态这么强大吗,那如果一个操作系统里有很多的D状态,这怎么办呢?
- 这个同学你问得很好,确实这个D状态的话操作系统是没有办法将其杀掉的,而是要等到磁盘写入完毕或者什么事情执行完毕后其才会去自动结束这个进程,或者是在外部断电、直接拔掉电源即可
- 一般一个系统里面如果存在D状态的话,那这个系统中的磁盘压力已经非常大了;如果存在多个D状态的话,则表示这个系统已经是非常危险了,最好马上重装一下系统💻
不过呢这个[D]
就没办法在这里给读者演示了,因为D状态的进程只有处于高IO的情况才可以演示。有兴趣的可以去研究一下Linux下的命令dd
4、停止状态T
好,接下去呢我们来讲讲【停止状态T】
- 首先我们要通过下面这句命令来查看一下对应的进程信号
kill -l
- 此处我们要使用到的是18、19号信号
- 接下去我们就来试一试如何让这个进程暂停之后又重新启动会是怎样的
- 所以我们来总结一下
- 暂停进程
kill -19 PID
- 启动进程
kill -18 PID
💬 所以呢,如果我们要将一个进程给终止的话,发送19号信号即可,要让其继续启动起来,则发起18号信号
那我现在要问了,这个T停止状态和S睡眠状态有什么不同呢?
- stopped状态进程 完全暂停了, 其不会再接收任何信号了
- 一个进程通过stopped状态可以控制另一个
- S和D一定是在等待某种资源,而T状态可能在等待某种资源,也可能被其他进程控制
5、进程跟踪状态t
接下去呢,我们再来说说进程的跟踪状态t,还记得我们在基础篇中所学习的 GDB调试 吗?
- 首先我们在正常状态下查看这个进程的状态,其为
S
睡眠状态。接下去呢我进行了【gdb】调试,在行内打上断点后,输入r
运行起来后,我们再去查看这个进程的状态时,就发现其状态变成了t
,原因就在于这个进程在调试的时候停了下来
6、死亡状态X
对于【死亡状态X】来说呢,这个状态只是一个返回状态,你不会在任务列表里看到这个状态🙅
- 那其实就是我们在上面所讲到的这个 杀进程
kill -9 PID
7、僵死状态Z —— 两个特殊进程
接下去我们来介绍一种状态叫做【僵死状态Z】,对于这个状态,我们要涉及到两个特殊的进程叫做 僵尸进程 与 孤儿进程
① 僵尸进程
💬 首先我们要来介绍的是僵尸进程,这里呢通过一个故事来进行引入
- 你呢,很喜欢晨跑🏃,这一天早晨6点又起来跑步了,当你路过一个公园的时候,遇到了一个晨练的老大爷,此时突然他就倒了下来。那你此时就非常担惊受怕了,马上拨打了
110
和120
的电话,然后守在他的身边等待救援来到,周边的人看到也都纷纷围了过来,其中不免有一些人会紧急救援。不过等了一会这个人就没了呼吸🖤
- 过了十几分钟后,救护车和警车都来了,警察先封锁了,让法医过来检验一下其状况,就说:“这个人已经没救了,赶紧通知家属准备后事吧~。”
好,我们回归正题,来说说这个【僵尸进程】
- 因为在救护车来之前这个人其实就已经死亡了,但是其状态还没有被检测,当前并不知道它的死因,所以我们操作系统就可能会维护当前的这个状态,这个状态即为
Z状态
,即[僵死状态]
💬 那我现在想问了:有一个进程暂时退出了,它要将它的状态暂时维持一段时间,问题是它维持给谁看呢?
- 答:父进程!当一个进程退出的时候,那最关心它的便是【父进程】。因为这个父进程费了很大的劲才将这个子进程
fork
出来,此时呢它突然挂掉了,那么此时父进程就必须去关心一下对应的子进程退出时的原因 - 不过光凭我们现在所学习的知识还无法去查看这个,等我们后面讲到
wait()
系统调用的时候再去做一定的介绍
就上面这样生冷的文字来叙述还不太行,接下去我们通过实际的案例来观察一下🔍
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 5 int main(void)6 {7 pid_t id = fork();8 if(id == 0)9 {10 // child11 int cnt = 5;12 while(cnt)13 {14 printf("I am child, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);15 sleep(1);16 17 cnt--;18 } 19 exit(0);20 }21 else22 {23 // father 24 while(1)25 {26 printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());27 sleep(1);28 }29 }30 return 0;31 }
- 运行起来可以看到,在一开始的 5s 内,父子进程还是同时在跑的,但是当子进程在循环结束后退出了。不过此时呢父进程还是运行并没有退出。所以我们在右侧查看这两个进程的状态时间就发生了一定的变化
- 仔细地来看一下这个状态的变化,其中【PID】为
5486
,其父进程为5485
,一开始它们均为S睡眠状态
,但是呢到了后面子进程的状态就变成了Z僵死状态
。也就意味着此时子进程已经死亡了,但是呢父进程还不知道 - 这里还可以通过右侧的这个
<defunct>
这个来看,它表示【失效的、不再存在的】
所以我们总结一下:
💬 进程一般退出的时候,如果父进程没有主动回收子进程信息,子进程会一直让自己处于Z状态,进程的相关资源尤其是 task_struct
结构体不能被释放
那对于上面的这种子进程,我们就将其称作为是【僵尸进程】,不过呢这种进程是存在一定危害的!
- 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间
- 那么对于上面的这种危害我们就称作为是【内存泄漏】,要如何去进行避免呢,之后的文章会做讲解~
② 孤儿进程
- 上面我们讲到,当一个子进程突然退出但是父进程并没有去主动回收的话,那么此时这个子进程就会变成【僵尸进程】
- 那看到下面我们将这个进程突然终止之后,父子进程都不见了
💬 那此时我想问:这个父进程突然之间退出了,但是呢它的父进程并不知晓,那为何这个父进程没有处于【僵尸状态Z
】呢?
- 因为当前这个进程是
bash
的子进程,当其一退出之后,bash
就将其回收了。可是这个子进程为什么也没了呢?这个的话我们就要聊聊【孤儿进程】了
接下去我们来将上面测试僵尸进程的代码做个修改,让父进程先于子进程退出
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 5 int main(void)6 {7 pid_t id = fork();8 if(id == 0)9 {10 // child11 int cnt = 500;12 while(cnt)13 {14 printf("I am child, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);15 sleep(1);16 17 cnt--;18 }19 exit(0);20 }21 else22 {23 int cnt = 5;24 // father 25 while(cnt--) 26 {27 printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());28 sleep(1);29 }30 }31 return 0;32 }
- 然后通过进程状态追踪来看看,我们观察到一开始两个父进程和子进程是同步在走的,但是呢过了一会父进程终止了,只有子进程还在跑,此时我们需要留意的不是其进程状态,而是这个子进程的
PPID
即父进程的PID
,它由原先的【19956】变成了【1】,这是为什么呢?
- 这里的话就要给读者普及一下了,对于
PID
的值为1的进程,我们一般将其称作为是【系统进程】。我们可以使用下面这句指令去查找一下
ps ajx | head -1 && ps ajx | grep systemd
- 然后我们看到这个【系统进程】的
PID
即为1
那为何会出现上面这种现象呢?是这个子进程突然换父进程了吗?
- 对于父子进程来说,如果父进程先于子进程结束的话,那么这个子进程就会被称作为是【孤儿进程】,对于孤儿进程来说呢,它会有1号进程即【系统进程】所领养,因为此时这个进程没有了父进程了,所以需要有人去带领它
💬 但是这个孤儿进程为什么要被领养呢?
- 其实很简单,既然它是一个进程的话,最终都是要退出的,但是没了父亲的话就只能由系统进程来进行维护了
那其实我们就可以解释上面的事情了
- 当这个父进程退出的时候,它是被
bash
给回收了,那么对于父进程的子进程来说,我们刚才谈到了,如果一个进程的父进程先于子进程结束的话,那么在这一刻这个子进程立马变成了【孤儿进程】,被系统领养并回收了,所以我们就看不到了
四、总结与提炼
最后来总结一下本文所学习的内容📖
- 在本文中,我们主要讲解了两个大点,一个是在操作系统中所常见的一些进程状态,它们分别为:运行状态、阻塞状态、挂起状态。
- 所被调度的、处于运行队列里的这些进程所处的状态我们称之为
运行状态R
- 处于等待队列中,同时在等待外设相应的进程所处的状态我们称之为
阻塞状态R
- 当把一个进程的 数据和代码都重新交换到外设当中,进程所处的状态我们称之为
挂起状态R
- 所被调度的、处于运行队列里的这些进程所处的状态我们称之为
- 接下去我们又介绍了在Linux系统下的七种进程状态,分别是:运行状态R、浅度睡眠状态S、深度睡眠状态D、停止状态T、进程跟踪状态t、死亡状态X、僵死状态Z
- 当一个进程没有在等待IO流的时候,其就会处于
运行状态R
- 使用
sleep()
函数可以很好地使一个进程处于浅度睡眠状态S
- 如果不想让一个进程在等待磁盘操作的时候被操作系统杀掉,则可让其处于
深度睡眠状态D
- 可以向一个进程发送【19】号信号使其暂停,处于
停止状态T
继续发送【18】号信号的话则可以使其重新启动 - 在【gdb】的环境下去运行一个断点的话则可以使其处于
进程跟踪状态t
- 使用
kill -9 PID
就可以杀掉一个进程,使其处于死亡状态X
- 如果让一个子进程在父进程不知晓的时候退出可,那么其就会处于
僵死状态Z
,变为【僵尸进程】;若是在父子进程中父进程先于子进程退出的话,那么这个子进程就会变成【孤儿进程】
- 当一个进程没有在等待IO流的时候,其就会处于
以上就是本文要介绍的所有内容,感谢您的阅读🌹
相关文章:

一探Linux下的七大进程状态
文章目录 一、前言二、操作系统学科下的进程状态1、运行状态2、阻塞状态3、挂起状态 三、Linux下的7种进程状态1、运行状态R2、浅度睡眠状态S3、深度睡眠状态D一场有趣的官司 4、停止状态T5、进程跟踪状态t6、死亡状态X7、僵死状态Z —— 两个特殊进程① 僵尸进程② 孤儿进程 四…...

香港站群服务器为什么适合seo优化?
香港站群为什么适合seo优化?本文主要从以下四点出发进行原因阐述。 1.香港站群服务器的优势 2.香港站群服务器与国内服务器的对比 3.多IP站群服务器的优势 4.香港站群服务器在SEO优化中的注意事项 1.香港站群服务器的优势 香港站群服务器是为了满足企业SEO优化需求而提供…...

虚拟机内搭建CTFd平台搭建及CTF题库部署,局域网内机器可以访问
一、虚拟机环境搭建 1、安装docker、git、docker-compose ubuntu: sudo apt-get update #更新系统 sudo apt-get -y install docker.io #安装docker sudo apt-get -y install git #安装git sudo apt-get -y install python3-pip #安装pip3 sudo pip install dock…...

qq录屏怎么弄?手把手教会你!
“有没有人知道qq怎么录屏呀,听说qq可以录屏,刚好最近需要录制屏幕,就想用qq去录,但是找了很久,都没找到,有人知道吗,谢谢了。” 在如今数字化时代,屏幕录制已成为广泛使用的工具。…...

一文读懂c++语言
一文读懂C语言 C的发展C的设计目标C的特性C的挑战 C的发展 C是一种通用的、高级的编程语言,它是C语言的扩展。C由Bjarne Stroustrup于1983年首次引入,并在之后的几十年中不断发展壮大。C被广泛应用于各种领域,包括系统开发、游戏开发、嵌入式…...
BERT数据处理,模型,预训练
代码来自李沐老师《动手学pytorch》 在数据处理时,首先执行以下代码 def load_data_wiki(batch_size, max_len):"""加载WikiText-2数据集"""num_workers d2l.get_dataloader_workers()data_dir d2l.download_extract(wikitext-2, w…...

Oracle将与Kubernetes合作推出DevOps解决方案!
导读Oracle想成为云计算领域的巨头,但它不是推出自己品牌的云DevOps软件,而是将与CoreOS在Kubernetes端展开合作。七年前,Oracle想要成为Linux领域的一家重量级公司。于是,Oracle主席拉里埃利森(Larry Ellison…...
微服务与Nacos概述-4
限流规则配置 每次服务重启后 之前配置的限流规则就会被清空因为是内存态的规则对象,所以就要用到Sentinel一个特性ReadableDataSource 获取文件、数据库或者配置中心是限流规则 依赖:spring-cloud-alibaba-sentinel-datasource 通过文件读取限流规则…...

Streamlit 讲解专栏(九):深入探索布局和容器
文章目录 1 前言2 st.sidebar - 在侧边栏增添交互元素2.1 将交互元素添加至侧边栏2.2 示例:在侧边栏添加选择框和单选按钮2.3 特殊元素的注意事项 3 st.columns - 并排布局多元素容器3.1 插入并排布局的容器3.2 嵌套限制 4 st.tabs - 以选项卡形式布局多元素容器4.1…...

使用cloud-int部署nginx
参考 azure创建虚拟机,创建虚拟机注意入站端口规则开放80端口,高级中使用自定义数据,初始化虚拟机,安装nginx 连接CLI,验证是否安装成功 访问虚拟机IP查看是否部署成功 参考文档: https://learn.microsoft.com/zh-cn…...

定量分析计算51单片机复位电路工作原理 怎么计算单片机复位电容和电阻大小
下面画出等效电路图 可以知道单片机内必然有一个电阻RX,为了简化分析,我们假设他是线性电阻(不带电容,电感的支路) 还有一个基础知识: 电容器的充电放电曲线: 还需要知道电容电压的变化是连续…...

消息队列相关面试题
巩固基础,砥砺前行 。 只有不断重复,才能做到超越自己。 能坚持把简单的事情做到极致,也是不容易的。 面试题 项目上用过消息队列吗?用过哪些?当初选型基于什么考虑的呢? 面试官心理分析 第一࿰…...
33 | 美国总统数据分析
在这个数据分析项目中,利用Pandas等Python库对美国2020年7月22日至2020年8月20日期间的超过75万条捐赠数据进行了深入的探索和分析。通过这一分析,他们揭示了这段时间内美国选民对总统候选人的偏好和捐款情况。以下是对文章中的主要步骤和内容的进一步描述: 数据集处理: 作…...
每日一题之常见的排序算法
常见的排序算法 排序是最常用的算法,常见的排序算法有冒泡排序、选择排序、插入排序、快速排序、希尔排序和归并排序。除此之外,还有桶排序、堆排序、基数排序和计数排序。 1、冒泡排序 冒泡排序就是把小的元素往前放或大的元素往后放,比较…...

JVM 类加载和垃圾回收
JVM 1. 类加载1.1 类加载过程1.2 双亲委派模型 2. 垃圾回收机制2.1 死亡对象的判断算法2.2 垃圾回收算法 1. 类加载 1.1 类加载过程 对应一个类来说, 它的生命周期是这样的: 其中前 5 步是固定的顺序并且也是类加载的过程,其中中间的 3 步我们都属于连接…...
C++ 多线程
C 多线程 多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序 一般情况下,两种类型的多任务处理:基于进程和基于线程 基于进程的多任务处理是程序的并发执行基于线程的多任务处理是同一程序的片段的并发…...

深入理解JVM之.intern()的用法
intern只在常量池里记录首次出现的实例引用 来看一段代码 public class RuntimeConstantPoolOOM {public static void main(String[] args) {String str1 new StringBuilder("计算机").append("软件").toString();System.out.println(str1.intern() st…...

idea报“Could not autowire. No beans of ‘UserMapper‘ type found. ”错解决办法
原因和解决办法 1.原因 idea具有检测功能,接口不能直接创建bean的,需要用动态代理技术来解决。 2.解决办法 1.修改idea的配置 1.点击file,选择setting 2.搜索inspections,找到Spring 3.找到Spring子目录下的Springcore 4.在Springcore的子目录下…...
QEMU源码全解析35 —— Machine(5)
接前一篇文章:QEMU源码全解析34 —— Machine(4) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社 特此致谢! 上回书说到有3…...

SpringBoot对一个URL通过method(GET、POST、PUT、DELETE)实现增删改查操作
目录 1. rest风格基础2. 开启方法3. 实战练习 1. rest风格基础 我们都知道GET、POST、PUT、DELETE分别对应查、增、改、删除 虽然Postman这些工具可以直接发送GET、POST、PUT、DELETE请求。但是RequestMapping并不支持PUT和DELETE请求操作。需要我们手动开启 2. 开启方法 P…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...