当前位置: 首页 > news >正文

【Linux】进程的状态(运行、阻塞、挂起)详解,揭开孤儿进程和僵尸进程的面纱,一篇文章万字讲透!!!!进程的学习②

目录

1.进程排队

时间片

时间片的分配

结构体内存对齐

偏移量补充

对齐规则 

为什么会有对齐

2.操作系统学科层面对进程状态的理解

2.1进程的状态理解

①我们说所谓的状态就是一个整型变量,是task_struct中的一个整型变量

②.状态决定了接下来的动作

2.2运行状态

2.3 阻塞状态 

2.4 挂起状态

3.linux下具体的进程状态(重要)

3.1 R和s 

(top)命令

 +:前后台程序

3.2 休眠状态s

3.3 disk sleep     不可中断睡眠也叫作深度睡眠。

3.4 T状态:进程变成暂停状态

3.5 t状态

3.6 z状态:僵尸状态

4. 孤儿进程

5.结语


1.进程排队

狭义上讲,进程 = task_struct + 可执行程序。

①当一个进程加载到内存的时候,首先这个进程不是一直在内存中运行的也不是一直在运行的。我们启动一个软件,并不是说我们启动这个软件,他就是一直被运行的,他可能因为要等待某种资源加载而处于某种等待状态。

等待键盘资源输入,所以进程卡住等待我们输入:

②即使进程被cpu调度,也不是一直在运行的。

 当我们写一个while死循环的程序,交给cpu去运行,cpu可能被打满,如果cpu只是执行我们这个程序,那么就意味着这个程序会一直在cpu上运行,没有结束就从cpu上下不来,其他进程不可能被调度,那么其他所有进程跑不了,但是事实是:可能此时会卡,但是其他进程还是正常运行,这说明进程不是放在cpu就要等执行完才下来,当代计算机都支持一个时间片的概念,cpu会给你这个程序一个运行时间,你上来就跑这么长时间,跑完就走,还要让其他进程执行,比如这个时间为1毫秒,1毫秒就是时间片.

时间片

时间片(timeslice)又称为“量子(quantum)”或“处理器片(processor slice)”是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间(在抢占内核中是:从进程开始运行直到被抢占的时间)。现代操作系统(如:Windows、Linux、Mac OS X等)允许同时运行多个进程 —— 例如,你可以在打开音乐播放器听音乐的同时用浏览器浏览网页并下载文件。事实上,虽然一台计算机通常可能有多个CPU,但是同一个CPU永远不可能真正地同时运行多个任务。在只考虑一个CPU的情况下,这些进程“看起来像”同时运行的,实则是轮番穿插地运行,由于时间片通常很短(在Linux上为5ms-800ms),用户不会感觉到。

时间片由操作系统内核的调度程序分配给每个进程。首先,内核会给每个进程分配相等的初始时间片,然后每个进程轮番地执行相应的时间,当所有进程都处于时间片耗尽的状态时,内核会重新为每个进程计算并分配时间片,如此往复。

时间片的分配

通常状况下,一个系统中所有的进程被分配到的时间片长短并不是相等的,尽管初始时间片基本相等(在Linux系统中,初始时间片也不相等,而是各自父进程的一半),系统通过测量进程处于“睡眠”和“正在运行”状态的时间长短来计算每个进程的交互性,交互性和每个进程预设的静态优先级(Nice值)的叠加即是动态优先级,动态优先级按比例缩放就是要分配给那个进程时间片的长短。一般地,为了获得较快的响应速度,交互性强的进程(即趋向于IO消耗型)被分配到的时间片要长于交互性弱的(趋向于处理器消耗型)进程。(引自百度百科)

所谓进程排队,一定是为了等待某种资,可以包括cpu,键盘、磁盘、等等...........

而内存中有很多的进程队列,进程排队是经常的进程描述块在排队。就比如我们去找工作时,投递简历,简历在hr处排队,拿到誰的简历了,就让相应的人来,是简历在排队。而简历就可以说是描述人的工作属性的一个结构体或者类。。所以只要是排队,一定是进程的task_struct在排队。 而一个task_struct可以被链入多种数据结构,linux中是被链入双链表中的,linux实现双链表的方式和我们c语言中是不大一样的:

首先在pcb中定义了这样一个结构体:

struct listnode
{struct listnode *next;
struct listnode *prev;
};

在每个进程的pcb中

我们通过链式对象找到listnode对象,那么还有很多的属性怎么访问呢。原理

struct listnode 是 task_struct中的一个变量也是一个地址。现在就相当于我们知道了结构体中一个变量的地址如何知道其他成员的地址呢。

结构体偏移量:补一下结构体偏移

了解的伙伴可以直接跳过

结构体内存对齐

题型考察结构体的大小,我们来看一下例子引入,请问如下这段代码输出分别为什么:

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

明明是两个一样的结构体,为什么却不一样大,要知道答案我们就要知道结构体的大小是如何计算的,结构体大小的计算并不是单单就靠结构体内部元素的类型大小来决定,让我们来看一下:

偏移量补充

 offsetof()    这个宏可以计算结构体某一个成员相较于起始位置的偏移量

头文件:stddef.h

需要在宏中传入的是:结构体类型和结构体变量名,下面我们来计算一下结构体s1中成员的偏移量和S2结构体的偏移量

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
int main()
{/*printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));*/printf("%d\n", offsetof(struct S1, c1));printf("%d\n", offsetof(struct S1, i));printf("%d\n", offsetof(struct S1, c2));return 0;
}

对齐规则 

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8 Linux中没有默认对齐数,对齐数就是成员自身的大小

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。所有成员对齐数的最大值。

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

我们来看一下图解S1的对齐:

 

对对齐规则第四条的解释:

让我们来看一下结构体嵌套的对齐算法:

 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

看一下这段代码输出风别为多少

struct S3
{double d;char c;int i;
};struct S4
{char c1;struct S3 s3;double d;
};int main()
{printf("%d\n", sizeof(struct S3));printf("%d\n", sizeof(struct S4));return 0;
}

 

如果出现数组,就当做多个同类型数据处理

为什么会有对齐

大部分的参考资料都是如是说的:

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。比如某些平台规定整型必须存放在4的整数倍地址处。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访 问。

虽然浪费了一些空间,但是换来了访问效率的提升

总体来说: 结构体的内存对齐是拿空间来换取时间的做法。 那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到: 让占用空间小的成员尽量集中在一起。​​​​​​​ 

 了解了结构体偏移量,就可以利用偏移量,计算我们的task_struct的地址了:

&c = &首元素+偏移量。结构体中成员越靠后地址越大

一个整型四个字节,每个字节一个地址,拿到的是最小的,因为有类型,也就是首地址+偏移量拿到4个字节的内容。数组访问,结构体成员访问底层都是这个样子。所以这里也是一样,我们可以使用偏移量来访问task_struct中所有的的其他属性内容。

&task_struct = &struct listnode+偏移量,那么这个偏移零的算法为:设我们一个节点为n

&((task_struct*)0->n);

&n-&((task_struct*)0->n) = 开始

(task_struct)(&n-&((task_struct*)0->n)) 得到task_struct地址

这既是linux内核的实现,会是一个宏的形式,这里是介绍会忽略掉一些类型转换。

那么我们说再全局层面上,所有的进程的pcb都要被链接入一个链表,方便我们的操作系统进行管理,那么,当我们的经进程需要排队运行的时候,需不要单独将进程从整体的双链表上取下来单独放入排队等待这个队列里面呢,答案是不用,

原因:誰规定我们的pcb里面只能有一个listnode这种结构体呢,没有人规定,那么我们的pcb中就可以有多个这种结构体,一个用于将这个task_struct链接入整体的双链表中进行操作系统级别的管理,其他剩余的可以在不把我们的单个task_struct拿下来的前提下再链接入其他的数据结构,比如进程排队的队列等等等等。

这个就叫一个task——struct可以被 链接进入多种数据结构的原因。

那么当我们的操作系统删除一个进程的时候,一定会伴生着将当前的进程从链表、队列中移除,但是所有的移除和task_struct没有关系,只用移除struct listnode.

这就是进程排队,那么进程排队是去哪里排队呢?接下来看进程状态

2.操作系统学科层面对进程状态的理解

首先教材删上的表述:

对于初学真的不好理解

2.1进程的状态理解

①我们说所谓的状态就是一个整型变量,是task_struct中的一个整型变量

#define  NEW 1
#define  READY 2#define  RUNING 3#define  BLOCK 4 1

我们进程的task_struct中

task_struct
{int status;
};

也就是说我们未来进程的状态就取决于这个ststus变量里面保持的是数字几,。

②.状态决定了接下来的动作

今天状态不好今天想摆烂,今天状态好,库弛库弛的写几套卷子,所以状态决定了对象的后续动作。linux中存在很多的进程,每一个进程都有一个状态,那么就可能存在多个进程都要根据他的状态执行后续的动作,而且还必然可能存在多个进程的状态是一样的,(虽然进程很多,状态类型也多),所以就需要进程排队

2.2运行状态

一个cpu一个运行队列,(如果电脑中有两个cpu,会有两个运行队列,操作系统在内核编译的时候,识别到两个cpu,就会创建两个队列,当运行多个进程的时候,原则上,操作系统是要均衡的将进程负载到两个cpu上,这也成为SMP,负载均衡式的给两个cpu平均分配资源,但是值得注意的是,一个进程这次调度在这个cpu上,下一次还是被这个cpu调度。)当我们的某个或者某些进程的资源的准备OK了,那么就可以进进程放入cpu的运行队列了,将进程的task——struct放入运行队列:

(关于怎么排,在优先级内容里面讲解)

 只要放在cpu上对应的运行队列中1,我们就把该进程当前状态称为运行状态

(至于运行队列里面保存的是pcb结构体还是只是节点struct listnode ,这和具体的系统和设备有关,有些操作系统可能用的指针或者一样的node,但是我们只要能够理解要的是链接结构就OK了。)

解释:

一个进程是不是只有在cpu上跑的时候才是运行状态,对吗?这样的操作系统有,不过是在教材的描述里面,这样的描述是在教材里面的描述,当然,在cpu上运行当然是运行状态,不过主流的操作系统,只要在运行队列里面都可以叫做运行状态。所以,所谓的创建和就绪在实际的操作系统中并没有实现,要运行就放在运行队列,不运行就放在常量队列里面。所以r状态就表明进程已经准备好随时被调度了,所以就绪和执行被二合一,因为这两个独立的状态看不到。

2.3 阻塞状态 

以硬件为例子:

os对硬件进行管理

先对硬件的属性进行描述

struct device
{
int tyoe;//1表示键盘,2,表示网卡,3表示磁盘......
//设备的操作方法,
//设备的状态
struct listnode n;
:::
:::
:::
};

在将各个设备使用数据结组织起来

和进程的管理方式一模一样,这就是操作系统为了对硬件做出管理做出的数据结构描述对象,

当前假设我们的计算机内部是这个样子:

现在比如说我们运行这个程序:

当我们程序运行起来,是不是就意味着此时进程需要等待键盘输入,不能继续往下面执行了,除非键盘输入 。所以,此时操作系统就来了,这个进程在等待什资源,就将这个进程的状态从运行状态改为非运行状态,我们可以暂时称为阻塞状态,然后将这个进程放到我们对应设备资源的队列当中。我们的cpu也是属于设备的,只不过是一个跑得快的设备,那么cpu可以有进程的运行列表队列,我们其他的设备一样的可以有我们的进程队列,当我们的进程需要对应的设备资源的时候,这个时候要等待这个设备就绪(键盘输入)才能继续执行,进程不能继续执行就从运行队列连入到我们的设备进程队列里面,因为可能需要这个设备资源的进程不止这一个。

接下来,cpu就不会调度这些连入底层设备调度队列里面的进程了,cpu只会调度在运行队列里面的进程,所以这个进程后面代码不执行了,也就是程序卡在这个地方等待输入。当我们在键盘输入数据了,而我们的键盘输入没有数据,也就是硬件的就绪状态,只有我们的操作系统清楚,操作系统可是我们硬件的管理者,所有用户按键过后,键盘工作OK了,操作系统就将这个进程将这个进程移到cpu的运行队列里面,将阻塞状态改为运行状态,我们操作系统的工作就完成了,剩下的就交给cpu去调度。(以上对两个进程pcb描述的移动,只是形象化的说法,我们进程的pcb是不会也不能删除的,只不过的是改变pcb里面对应的链接字段也就是listnode)

我们不要用人类的感受衡量计算机的速度,此时再次执行到输入的话,我们的设备状态就绪(理解为操作系统已经将数据从外设搬到内存了),有数据了,就可以继续往下执行。

结论:

当我们的进程在进行等待软硬件资源的,资源如果没有就绪,我们的进程task_struct只能将自己设置为阻塞状态,将自己的pcb连入等待的资源提供的等待队列中。

所以状态的变迁,引起的是pcb搬迁到不同的队列中。

2.4 挂起状态

挂起状态在linux中并不是一个比较常见的状态,大部分情况下计算机极少出现这种情况

挂起状态的前提:

计算机资源比较吃紧,也就是说计算机的内存资源比较吃紧了,(并不排除其他情况)

进程在阻塞状态的时候,我们的程序的代码和数据是不会被执行和访问的,那么这个时候,这个进程又不会被调度,但是进程的代码和数据又占据着内存的空间,如果此时计算机内存已经很吃紧了,操作系统很大可能就会将这个进程的代码和数据放回磁盘中,当这个进程快要被调度的时候,再拿回内存。这个我们称之为:阻塞挂起。还有运行挂起等等

(进程会等待某种资源,此时进程大概率是不会被执行的,这种资源如果短期内不会就行,进程就在阻塞队列等待,如果突然计算机内存资源非常吃紧,cpu和整机资源越来越少,甚至严重不足,已经无法为上层运行分配内存,此时对于计算机来说即将面临大量操作的失败,操作向上提供好的环境其他就无了,操作系统也面临着崩溃问题,要么摆烂崩溃,要么搞点内存,所以操作系统就会对整个机器的每个内存做说明,有些内存现在就要用,有些现在不用却占着内存比如阻塞状态进程,那么就造成资源的浪费,所以操作系统就尝试将这个进程的代码和数据交换到磁盘中,磁盘有固定的区域,是为了在内存紧张的时候用于和操作系统进行数据的换入换出叫做swap分区,

真正当内存吃紧,操作系统会将很多很多进程的代码和数据换入换出,节省空间,还有一些为网络预留的空间也会被释放,也就是说整个系统中能被释放的都要释放。传出到磁盘叫换出,等要用或者即将用到的时候再加载回来叫换入,一但对应的进程对应的数据和代码不在内存中,就称当前状态为挂起状态。特指阻塞挂起)

所以所谓挂起,就是将进程的代码和数据写入到外部的磁盘,当进程要被调度的时候再放回来,这是临时的腾空间,这是操作系统在内存不足的情况下进行辗转腾挪,想把空间变大一点,要是内存分配不足,操作系统挂了,整个就挂了,慢点总比挂了好。

思考:,换入换出的时候,pcb数据结构是不会被换出的,因为操作系统就是根据属性知道的进程处于什么状态。

创建进程是先创建pcb还是先将进程的代码和数据先加载到内存?

如果我们的内存很小,比如只有16G,但是我们现在要运行80g的游戏,

只会先加载一部分, 自己的代码和数据刚开始可能非常大,根本加载不完,只会先加载一批,那么当前进程该不该运行呢?先将pcb保存内存里,没有代码和数据也不影响,未来也可以换入,再调度执行,所以双击运行的时候,会先创建管理字段,创建完,内存和字段都还不会加载到内存。

辅助理解例子:

我们高考毕业的时候,人还没有到学校,但是学校在录取我们的时候就已经将我们的档案拿走了,等我们九月份开学的时候,我们该在那个班级,学号是多少,都被排得明明白白,就好比,进程运行的时候,先将pcb建立出来,再将数据等加载,然后就静等调度了。

既然数据要换入换出,可以将磁盘分区搞大些吗?绝对可以,操作系统hold得住,但是一般在系统设置的时候,内存分区的大小一般为内存的一半或者就是内存的大小,最多不超过内存的两倍。

理由:整体的换入换出是将数据进行拷贝的过程,冯诺依曼体系告诉我们,计算机当中,本质进行数据访问访问外设速度是比较慢的,在阻塞挂起时,换入换出,使用效率换系统可用性,就是运行变慢和系统可用之间选择了系统可用。如果分区设置非常大,系统就会很依赖这个swap分区,想办法将这个用完,那么操作系统和外设的io操作比重就很高,效率就会越来越慢。

3.linux下具体的进程状态(重要)

具体看看具体操作系统下面什么是运行、什么是阻塞、挂起(代码中不是很好看就不展示)

linux内核代码对于进程的描述

/*
* 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 */
};
  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列 里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
  • Z(zombie)-僵尸进程 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态 

3.1 R和s 

编写一段代码如下,执行:

查看我们的进程:

stat就描述了当前进程的状态 

为什么程序在运行,状态却是S,睡眠状态

因为cpu执行我们这行输出的时间非常快,然后我们写了sleep,睡眠1秒,那么对于cpu来说,整个程序很长的时间都是在sleep,那么这个状态显示就不奇怪了

接下来修改一下代码,注释掉sleep

代码疯狂运行:

状态还是s 

因为,我们自己的代码中有printf,向显示器打印,这个程序在远端服务器跑的,当进行执行的时候,结果打印在我们的电脑上,printf是要访问外设的,不论是网络设备还是显示屏,都是要访问外设的,那么我们想打印的时候,我们的设备就绪了吗?,cpu可以比其他外设的速度快多了,第一次打印了,第二次能保证数据已经刷新到设备了吗?当执行打印代码,将数据执行到缓冲区,设备来得及刷新吗?不一定,所以printf里面就伴生着使得设备处于就绪状态的东西,显示是睡眠状态,就说明当期进程百分之95的时间都是在等待,只有百分之五的时间在运行。

我们再修改代码:

 

此时就是运行状态了(不访问任何外设还是个死循环)

在目前系统里,可能有多个进程,只有在运行队列里才是运行状态,大部分进程等待外设是非运行状态

(top)命令

为什么grep总是r

grep过滤也是一个进程,只有被调度时才可以帮我们打印过滤结果,把自己过滤出来,就相当于自己知道自己是什么状态,所以一睁眼就知道自己醒着。

 +:前后台程序

表示这个程序是前台进程还是后台进程(信号文章详细讲解)

一般程序在运行的时候,我们将程序称为前台程序

此时的状态为:s+

 我们执行一些指令时没有办法截停这个程序,当我们执行ctrc就可以截停

此时我们在执行程序命令后面+ &

 我们运行一些指令也是可以的:

发现按住ctrls 无法截停

 当前进程状态为:

就为s没有+号,这种进程我们就称为后台进程,前台进程可以被键盘直接杀掉,后台进程不可以,只能通过kill -9 进程pid 来杀死

批量化下载等可以在指令后面+&

3.2 休眠状态s

编写代码如下:

 

在等待键盘输入,linux中的休眠就等价于操作系统学科中的阻塞状态。

阻塞描述的是宏观,在linux中是s状态,但是不仅仅只有上述一种阻塞s状态, 

这种进程可以通过ctrlc结束,所以我们也将linux中的这种状态称为可中断睡眠,也称为浅度睡眠

3.3 disk sleep     不可中断睡眠也叫作深度睡眠。

举例来说当我们的进程要将一块很大的数据给我们的磁盘,磁盘比较慢,进程就一直等待等待,那么在内存比较吃紧的时候,操作系统也是会移除某些进程的,操作系统过来一看你什么也不干,还在这里占着内存,将移除了这个进程,然后此时如果我们磁盘出了异常,拷贝的数据找不到来头,无法继续拷贝就丢弃了,这不行,当这个进程一直等待,操作系统也不能将他移除,就叫深度睡眠。

3.4 T状态:进程变成暂停状态

某个进程要访问某种资源,但是呢某种资源又不能被这个进程所访问,操作系统又不想杀掉这个进程,但是为了不让这个进程做某些非法的操作,就将进程设置为暂停状态。

kill -l 命令·可以·查看信号

9号命令可以帮我们杀死一个进程

19号命令:给一个进程发送一个暂停信号

状态变为暂停,没有+号 ,进程一但暂停,就从前台转变为后台进程了。

18 号信号,进程继续启动:

 

3.5 t状态

 我们调试这段代码:

我们在输入出打上断点过后,让程序运行后我们看一下进程的状态

进程会变成t状态,也是一种暂停,是一种被追踪的状态 

等待我们输入或者按下n

 等待也是一种阻塞状态。

3.6 z状态:僵尸状态

进程创建的目的是让进程帮助完成某种任务,任务死掉了,我们要获取进程的退出数据,是正常退出还是遇到异常,退出后,代码和数据可以直接释放,因为进程死掉了,后续的代码不会再执行了,但是进程pcb不应该立马释放,应该缓一下,为了未来让系统或者其他进程获取一下他的退出数据,只有进程的退出数据被拿走之后,这个进程才是正常死亡状态,pcb才可以被释放。

我们将一个进程执行完毕,但是当前并没有去获取这个进程的退出数据时,我们将这个进程的状态称为僵尸状态。

通常情况下,是由父进程产生子进程,当子进程退出,父进程就必须去读取一下子进程的退出信息,怎么读取,不是目前所关心的话题,如果父进程不读取,这个子进程的代码和数据被释放,但是pcb不能被释放,其中包含了退出时进程的状态信息。

程序前五秒父进程和子进程一起运行,五秒过后,子进程退出, 就应该看见子进程变成僵尸

defunt就是无用的,失效的,死亡的意思 

进程退出,pcb不能释放,保存退出时的状态信息,等待父进程读取,进程的代码和数据合约先释放:

接下来我们让父进程读一下,然后看一下僵尸进程的消失:

前五秒父子进程同时运行,然后中间五秒子进程变成僵尸,十秒后,父进程回收子进程

为什么维持僵尸状态:

未来我们创建进程是希望这个进程给用户完成某种工作的,肯定要求一个结果,将子进程创建出来,那么子进程必须要有结果数据,这个数据是保存在pcb中,子进程结束,代码和数据可以释放,但是pcb还不能被释放

什么是z状态:

进程已经退出,但是当前进程的状态需要自己维持住,供上层读取,那么就必须处于z状态

只有当通过我们的方式读取pcb里面的退出信息了,才能变成x死亡状态

如果不读取,僵尸状态会一直存在,是会有影响的,一直存在疑问着task_struct会一直存在,维护这个对象都是要占据内存的。这块内存得不到释放又不能使用叫做内存泄漏。所以父进程必须等待,所有的进程都必须先到z,再到x。bash父进程会自动读取,其余父进程不确定,可能自己读取,可能像我们上面一样要手动读取等待。bash命令行会主动回收子进程状态,所以看不到。

那么这张图在我们linux下是适用的

4. 孤儿进程

修改代码如下:

 

父进程先退出,父进程很难查到自己的僵尸状态,bash会回收,那么父进程先退出了,为了子进程变僵尸,誰来回收呢? 

父进程先挂掉,子进程会被1号进程领养,我们可以理解为操作系统,或者说父进程先结束,子进程必须被操作系统领养,因为无主进程,一直僵尸浪费资源,我们将这种进程称之为孤儿进程。 还会将自己变成后台进程。

孤儿进程也是可以有子进程的。 

5.结语

今天的主要内容是进程的排队,进程状态在操作系统学科层次的理解和linux操作系统下的观察。扩展了很多内容,补充了僵尸进程和孤儿进程,后续进程的优先级和进程相关内容,欢迎大家关注。创作不易,如果大家觉得有所收获,欢迎关注,一起交流互进。 

相关文章:

【Linux】进程的状态(运行、阻塞、挂起)详解,揭开孤儿进程和僵尸进程的面纱,一篇文章万字讲透!!!!进程的学习②

目录 1.进程排队 时间片 时间片的分配 结构体内存对齐 偏移量补充 对齐规则 为什么会有对齐 2.操作系统学科层面对进程状态的理解 2.1进程的状态理解 ①我们说所谓的状态就是一个整型变量,是task_struct中的一个整型变量 ②.状态决定了接下来的动作 2.2运行状态 2.…...

前端js基础知识(八股文大全)

一、js的数据类型 值类型(基本类型):数字(Number)、字符串(String)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol,大数值类型(BigInt) 引用数据类型:对象(Object)、数组…...

316_C++_xml文件解析成map,可以放到表格上 + xml、xlsx文件互相解析

xml文件例如&#xff1a; <?xml version"1.0" encoding"UTF-8" standalone"yes"?> <TrTable> <tr id"0" label"TR_PB_CH" text"CH%2"/> <tr id"4" label"TR_PB_CHN"…...

未来汽车硬件安全的需求(2)

目录 4.汽车安全控制器 4.1 TPM2.0 4.2 安全控制器的硬件保护措施 5. EVITA HSM和安全控制器结合 6.小结 4.汽车安全控制器 汽车安全控制器是用于汽车工业安全关键应用的微控制器。 他们的保护水平远远高于EVITA HSM。今天的典型应用是移动通信&#xff0c;V2X、SOTA、…...

html+javascript,用date完成,距离某一天还有多少天

图片展示: html代码 如下: <style>* {margin: 0;padding: 0;}.time-item {width: 500px;height: 45px;margin: 0 auto;}.time-item strong {background: orange;color: #fff;line-height: 100px;font-size: 40px;font-family: Arial;padding: 0 10px;margin-right: 10px…...

跟bug较劲的第n天,undefined === undefined

前情提要 场景复现 看到这张图片&#xff0c;有的同学也许不知道这个冷知识&#xff0c;分享一下&#xff0c;是因为我在开发过程中踩到的坑&#xff0c;花了三小时排查出问题的原因在这&#xff0c;你们说值不值。。。 我分享下我是怎么碰到的这个问题&#xff0c;下面看代码…...

数据结构_基于链表的通讯录

顺序表的源代码需要略作修改&#xff0c;如下 将数据类型改为通讯录的结构体。注释掉打印&#xff0c;查找的函数。 SList.h #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> #include<stdlib.h> #include<assert.h> #include"Contact.h"ty…...

jenkins+gitlab配置

汉化 1、安装Localization: Chinese (Simplified)插件 &#xff08;此处我已安装&#xff09; &#xff08;安装完成后重启jenkins服务即可实现汉化&#xff09; 新增用户权限配置 1、安装插件 Role-based Authorization Strategy 2、全局安全配置 3、配置角色权限 4、新建…...

【Labview】虚拟仪器技术

一、背景知识 1.1 虚拟仪器的定义、组成和应用 虚拟仪器的特点 虚拟仪器的突出特征为“硬件功能软件化”&#xff0c;虚拟仪器是在计算机上显示仪器面板&#xff0c;将硬件电路完成信号调理和处理功能由计算机程序完成。 虚拟仪器的组成 硬件软件 硬件是基础&#xff0c;负责将…...

IvorySQL 3.2原理解析|与Oracle 12c XML函数兼容性的实现机制

[发行日期&#xff1a;2024年4月11日] IvorySQL 3.2基于PostgreSQL 16.2&#xff0c;引入了多种Oracle XML函数的全面兼容性功能&#xff0c;同时修复了多个问题&#xff0c;更多信息请参考文档网站。 >>>新版本体验链接&#xff1a; https://docs.ivorysql.org/cn…...

SpringBoot + Dobbo + nacos

SpringBoot Dobbo nacos 一、nacos https://nacos.io/zh-cn/docs/quick-start.html 1、下载安装包 https://github.com/alibaba/nacos/releases/下载后在主目录下&#xff0c;创建一个logs的文件夹&#xff1a;用来存日志 2、启动nacos 在bin目录下打开cmd运行启动命令&a…...

学习笔记-微服务基础(黑马程序员)

框架 spring cloudspring cloud alibaba Eureka eureka-server 注册中心 eureka-client 客户端每30s发送心跳服务 服务消费者服务提供者 server 依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-star…...

每日Bug汇总--Day05

Bug汇总—Day05 一、项目运行报错 二、项目运行Bug 1、**问题描述&#xff1a;**前端将从后台查询的数据作为参数进行get请求&#xff0c;参数为空 原因分析&#xff1a; 这种写法可能只支全局的参数调用方法的传参响应 代码实现 if (this.jishiName) {this.$http({url…...

docker、ctr、crictl命令对比

命令dockerctr&#xff08;containerd&#xff09;crictl&#xff08;kubernetes&#xff09;查看运行的容器docker psctr task ls/ctr container lscrictl ps查看镜像docker imagesctr image lscrictl images查看容器日志docker logs无crictl logs查看容器数据信息docker insp…...

uniapp 编译后分包下静态图片404问题解决方案

如上图官方说明&#xff1a; 在分包下建立一个static文件夹即可&#xff1a; 分包内代码引用图片 <image src"/分包名称/img/图片名称"></image> <image src"/dataView/img/图片名称"></image>...

第十二届蓝桥杯大赛软件赛省赛Java 大学 B 组题解

1、ASC public class Main {public static void main(String[] args) {System.out.println(...

关于openai和chatgpt、gpt-4、PyTorch、TensorFlow 两者和Transformers的关系

近两年&#xff0c;随着人工智能的火爆&#xff0c;不论通过哪个渠道&#xff0c;相信我们都听说过openai、gpt等这类名词&#xff0c;那么它们到底是什么意思&#xff0c;请看下文。 openai:是一家人工智能公司&#xff1b; openai-api&#xff1a;是openai提供的api&#xf…...

C 共用体

共用体是一种特殊的数据类型&#xff0c;允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体&#xff0c;但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。 定义共用体 为了定义共用体&#xff0c;您必须使用 u…...

智能合约:未来数字经济的基石

智能合约是一种自动执行交易的计算机协议&#xff0c;它以代码形式规定了交易双方的权利和义务&#xff0c;具有高度的可靠性和安全性。随着数字经济的发展&#xff0c;智能合约的重要性日益凸显&#xff0c;将成为未来数字经济的基石。 首先&#xff0c;智能合约在金融领域的应…...

第十一届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

第十一届蓝桥杯大赛软件赛省赛C/C 大学 B 组 文章目录 第十一届蓝桥杯大赛软件赛省赛C/C 大学 B 组1、字串排序2、门牌制作3、既约分数4、蛇形填数5、跑步锻炼6、七段码7、成绩统计8、回文日期9、子串分值和10、平面切分 1、字串排序 // 转载博客链接 https://blog.csdn.net/we…...

Lua语法(三)——元表与元方法

参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 元表与元方法目录 简介正文元表元方法表相关常…...

Lua语法(五)——垃圾回收

参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 Lua语法 五——垃圾回收 垃圾回收弱引用表__m…...

已解决java.net.NoRouteToHostException: 无法到达主机异常的正确解决方法,亲测有效!!!

已解决java.net.NoRouteToHostException: 无法到达主机异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 检查网络连接 核实目标地址 检查防火墙和路由器规则 验证VPN/代理设置 修正网络配置 …...

代码随想录算法训练营第三十八天| 509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯

题目与题解 参考资料&#xff1a;动态规划基础 动态规划五步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 题目链接&#xff1a;​​​​​​​509. 斐波那契数 代码随想录题解&am…...

视频中会动的进度条

视频中会动的进度条 1.成果展示&#xff1a;2.步骤&#xff1a; 1.成果展示&#xff1a; 2.步骤&#xff1a;...

C++高级特性:柯里化过程与std::bind(六)

1、柯里化过程 1.1、operator()的引入 现在需要完成这样一个需求&#xff1a;有一个函数每次调用返回的结果不一样。例如&#xff1a;两次调用的返回值都不一样那么就可以达到这种目的 1.1.1、简单点的写法 可以给一个全局的变量&#xff08;静态变量&#xff09;&#xff…...

vmware虚拟机补救

更新了虚拟机里面工具和资料&#xff0c;进行了磁盘整理和压缩&#xff0c;虚拟机运行进不去系统了。 网站找的修复方法均不可行。补救措施&#xff1a;利用DiskGenius.exe&#xff08;要用高版本不然复制的时候就知道了&#xff09; DG1342.rar - 蓝奏云 加载虚拟硬盘 2008x…...

数据结构(算法)

总结&#xff0c;建议看EXCEL的《算法》页签&#xff0c;不然感觉有点乱 备注原理/步骤时间复杂度空间复杂度串的应用模式匹配简单/暴力O(mn) KMP  O(mn) 树的应用树哈夫曼树1、带权路径长度WPL 2、外部排序-最佳归并树1、哈夫曼树的度&#xff0c;只有0和m&#xff08;m叉…...

SpringCloud集成SkyWalking链路追踪并收集日志2

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…...

纯小白蓝桥杯备赛笔记--DAY4(数学数据结构图论)

文章目录 数学质因数分解辗转相除法求最大公约数最小公倍数&#xff1a;快速幂乘法逆元费马小定理 逆元乘法逆元素数判定与埃式筛法朴素素数判定法埃式筛法 图论并查集T3:真题--合根植物DijkstraFloyd 基础算法递归&#xff0c;循环&#xff0c;前缀和&#xff0c;差分STL 数学…...

如何查询网站空间商/手游推广代理平台有哪些

今天介绍一下lamp环境的配置。 服务器用的是阿里云的服务器。 L&#xff1a;centos、A&#xff1a;apache、M&#xff1a;mysql、P&#xff1a;php &#xff08;一&#xff09;安装apache 我们这里安装的是httpd &#xff08;1&#xff09;安装httpd # yum install httpd…...

wordpress的集成环境/网站推广的方式有哪些

转载于:https://www.cnblogs.com/jkwang/p/5841234.html...

网站建设创意报告书/百度排名软件

內核所在目錄&#xff1a;/usr/src/linux/ Pacman系統更新相關&#xff1a;pacman -Su 升級系統中所有已經安裝的包&#xff1b;pacman -Syu 升級系統同步倉庫數據&#xff1b;pacman -Sc 清理當前未被安裝的軟件包之緩存&#xff08;/var/cache/pacman/pkg&#xff09;&#x…...

网站手机版管理链接/今日国内新闻头条15条

在linux中如何查看服务器配置也是一个常常要用到的工作linux的硬件配置信息(除存储和外置设备dev)一般放置于/proc目录下&#xff0c;有如meminfo内存信息、cpuinfo主机CPU信息、version系统版本信息等1.比如查看服务器的内存信息cat /proc/meminfo或者free命令2.查看服务器cpu…...

国外网站 国内做镜像/电话投放小网站

题目要求&#xff1a; **要求写一个案例&#xff0c;使用三个JSlider分别选取R、G、B三原色的值&#xff0c;用户可以通过活动JSlider的滑块来动态的合成一种颜色&#xff0c;合成的颜色显示在界面上。**代码思路解析&#xff1a; 1、所需要的组件 (1)JFrame窗口(2)JLabel的RGB…...

asp.net 发布网站 ftp/个人网页设计作品欣赏

字符串函数 include<string.h> (1)strlen: strlen(字符串名)———代表的是取这个字符串的长度&#xff0c;不包括结尾的"\0". (2)strcmp: int strcmp(const char*s1,const char *s2)——用来比较这两个字符串&#xff0c;返回:(1).s1s2:0;(2).s1>s2:1;(3)…...