网站建设网上接单,郑州网站建设tpywlkj,贴吧 wordpress,营销神器官方网站文章目录 一、进程状态概述1.1 运行状态详解1.2 阻塞状态详解1.3 挂起状态详解 二、具体的Linux操作系统中的进程状态2.1 Linux内核源代码2.2 查看进程状态2.3 D磁盘休眠状态(Disk sleep)2.4 T停止状态(stopped) 三、僵尸进程3.1 僵尸进程危害总结 四、孤儿进程五、结语 一、进… 文章目录 一、进程状态概述1.1 运行状态详解1.2 阻塞状态详解1.3 挂起状态详解 二、具体的Linux操作系统中的进程状态2.1 Linux内核源代码2.2 查看进程状态2.3 D磁盘休眠状态(Disk sleep)2.4 T停止状态(stopped) 三、僵尸进程3.1 僵尸进程危害总结 四、孤儿进程五、结语 一、进程状态概述
进程状态是指在操作系统中一个进程所处的不同运行状态进程状态就决定了该进程接下来要执行什么任务。常见的进程状态有以下几种 新建状态进程被创建但还没有被操作系统接受和分配资源。 就绪状态进程已经获得了所需的资源并等待被调度执行。 运行状态进程正在执行指令占用CPU资源。 阻塞状态进程因等待某个事件如IO操作而暂时停止执行并释放CPU等资源。 终止状态进程执行完成或被终止释放所有资源。
1.1 运行状态详解
在上一篇文章【Linux取经路】揭秘进程的父与子提到过一般的计算机中只有一个 CPU而进程却可能有很多个这就注定了 CPU 是一个少量的资源对所有的进程来说运行的本质就是把它放到 CPU 上所以每个 CPU 都会维护一个运行队列CPU 以队列的形式对进程做调度。所有的进程要运行都要在运行队列中排队参与排队的是每个进程的 PCB 对象。所有在运行队列中的进程它们所处的状态就叫做运行态(R状态)。 一个进程只要把自己放到 CPU 上开始运行并不是一直要执行完毕才把自己放下来。如果一个进程被放到 CPU 上直到执行完毕才把自己放下来继续去执行其他进程那当我们的程序中写了一个 while 死循环出来在运行该程序的时候其他的应用就会卡住。但现实并不是这样我们写了一个 while 死循环其他程序照样可以正常运行。为了避免这种一个进程长时间占用 CPU 资源的情况出现提出了时间片的概念。
时间片是操作系统中任务调度算法的一种思想即将 CPU 的执行时间划分成固定长度的时间段每个时间段称为一个时间片。在每个时间片内操作系统将 CPU 分配给一个任务进行执行当时间片耗尽时操作系统会中断当前任务并将 CPU 分配给下一个任务。时间片一般是10毫秒左右所以在一个时间段内所有的进程代码都会被执行我们将这种情况叫做并发执行。这种情况下会有大量的把进程放上 CPU 和从 CPU 拿下来的动作这就叫做进程切换。
1.2 阻塞状态详解
最常见的阻塞状态就是一个进程需要通过键盘读取数据。当一个进程等待从键盘输入的过程此时该进程就处在阻塞状态。键盘是一种硬件在冯诺依曼结构体系中属于输入设备外设操作系统对硬件资源的管理是先描述再组织因此每一个硬件都会对应一个结构体对象该结构体对象中一定会维护一个等待队列当一个进程需要利用该硬件资源时进程的 PCB 对象就会被链入该等待队列此时进程就处于阻塞状态。 小Tips操作系统中的等待队列可能有成百上千个不仅每一种硬件有等待队列进程中也有等待队列可能会出现一个进程等待另一个进程结束后才能继续运行。不同的操作系统调度算法也会不同。
1.3 挂起状态详解
在一些操作系统的教材上还会出现挂起状态。无论是运行状态还是阻塞状态一个进程在没有被 CPU 调度的情况下它的代码和数据是处于空闲的即没有被使用。之前说过一个进程在内存中有它自己的代码和数据还有自己的 PCB 对象当内存空间告急时操作系统就会把这些没有被 CPU 调度的进程的代码和数据先放到磁盘中存储只留进程的 PCB 对象在队列中排队这种进程就处于挂起状态。 上面介绍的这些属于操作系统学科的理论知识不同的操作系统可能会有不同的实现方案下面我们来深入看看具体的 Linux 操作系统中有哪些进程状态。
小Tips挂起状态对用户是不可见的这是操作系统的一种行为。就像我们把钱存银行里我们并不知道银行把我们的钱拿去干嘛了银行可能把我们的钱借出去了或者给员工发工资了等等我们作为客户不得而知我们只知道如果存的是活期可以随时到银行把钱取出来如果存的是死期只有到期了才能取出来。
二、具体的Linux操作系统中的进程状态
为了弄明白正在运行的进程是什么意思我们需要知道进程的不同状态。一个进程可以有几个状态在 Linux 内核里进程有时候也被叫做任务。
2.1 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)意味着进程在等待事件完成这里的睡眠有时候也叫做可中断睡眠(interruptible sleep)它对应操作系统理论中的阻塞状态。 D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep)在这个状态的进程通常会等待 IO 的结束。 T停止状态(stopped)可以通过发送 SIGSTOP 信号给进程来T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。 X死亡状态(dead)这个状态只是一个返回状态你不会在任务列表里看到这个状态。
2.2 查看进程状态
先来看看下面这段代码执行起来后的进程状态。
int main()
{ while(1) { printf(Hello Linux\n!); } return 0;
} 可以看出这段代码执行起来后的进程状态是 S睡眠状态将 while 循环中的打印去掉再去执行代码看看进程状态。
int main()
{ while(1); return 0;
} 此时代码中只剩一个 while 死循环去执行这段代码进程状态变成了 R运行状态。为什么会出现在这种情况呢原因是 CPU 的执行速度是非常快的第一段代码中的 printf 是要去频繁的访问显示器设备而我们的显示器可能并不能被该进程直接去写入所以该进程大部时间都在显示器的等待队列里等待显示设备就绪因此最终查出来的进程状态是 S睡眠状态。当我们去掉 printf 之后该进程就不会去访问显示器设备始终都在运行队列里所以最终查出来的进程状态是 R运行状态。
小Tips查询结果中显示的表示该进程在前台运行这意味我们此时在 bash 命令行输指令是不会有任何反应的可以在输入指令的后面加上此时表示让该进程在后台运行要终止掉该进程只能通过指令kill -9 进程PID。
2.3 D磁盘休眠状态(Disk sleep)
D状态也是一种阻塞状态在 Linux 系统层面我们称作深度睡眠S状态称作浅度睡眠。浅度睡眠是可以被唤醒的即可以响应外部的变化我们可以通过 kill 指令其他进程将浅度睡眠的进程终止掉。下面通过一个情景剧来给大家介绍为什么要有 D 状态以及 D 状态的作用。
有这样一个场景一个进程需要向磁盘中写入大量数据。在正常情况下往磁盘中写入数据进程是需要等待的等磁盘写完后给进程一个信号然后进程才能继续去运行。有一天进程A就在向磁盘中写入大量数据磁盘在写入的过程中进程A就在内存中翘着二郎腿嗑着瓜子在等待磁盘写完了给它发信息此时路过的操作系统发现了进程A它对进程A说“我这内存压力都大的不行了你小子倒好占着内存不干正事还在这嗑瓜子”。于是乎操作系统就将进程A kill 掉了。此时磁盘傻眼了数据写到一半进程没了因为进程没了所以磁盘就把写入的数据删除了最终结果就是数据没有被写入磁盘。究竟是谁导致了这场悲剧的发生呢于是乎法官就出来它先审问操作系统进程是你 kill 掉的你怎么解释操作系统说我命苦呀我只是完成了我的本职工作呀为了给用户提供流畅的运行环境将一些进程 kill 掉是我的职责呀这不是我的问题呀。接着法官又来问磁盘数据是你丢失的你该如何解释磁盘说我祖祖辈辈都是这样工作的呀进程它让我写入数据结果自己不见了其它磁盘遇到这种情况也是将数据丢弃掉呀你如果判我有罪那岂不是我的父亲、母亲都有罪呀。最后法官来问进程A进程还没等法官开口就扑通跪下说法官大人您明察秋毫呀我才是被 kill 掉的那个我属于被害人呀我怎么会有罪呢。法官听了一圈感觉大家都没罪最终法官宣判了你们三个都没罪是制度问题回去我改改操作系统当进程在向磁盘中写入数据的时候任何人都不能将该进程 kill 掉。于是 D 状态就诞生了。当一个进程处于 D 状态的时候它不会响应任何请求任何人和操作系统都不能将该进程 kill 掉。
小Tips结束掉 D 状态的方法有两种一是等待某个条件满足如等待数据写完二是直接断电。如果被用户查到 D 状态的进程那就预示着这个操作系统离崩溃不远啦。所以 D 状态会有但是一般出现的时间都非常短。
2.4 T停止状态(stopped)
在 Linux 内核源代码中我们可以看到连个 T 状态一个是 T 一个是 t我们可以认为这两个 T 状态是一样的对于一个进程我们可以通过下面这条指令将它设置成停止状态。
kill -19 进程PID可以通过下面这条指令来结束停止状态。
kill -18 进程PID//小Tips结束停止状态的进程会到后台运行要终止掉这个进程只能通过 kill -9指令。T状态和S状态很像其中S状态的进程一定是在等待某种资源而T状态的进程可能是在等待某种资源也可能是在被其他进程控制。我们在打断点调试一段代码的时候该进程就会处于T状态。 三、僵尸进程
一个进程在退出时并不是立即将自己所有资源全部释放当一个进程退出时操作系统会把当前进程的各种信息维持一段时间这个状态就叫做 Z 僵尸状态。维持信息是给关心它的“人”也就是父进程来查看的。如果父进程一直没有来关心退出的子进程那么这个子进程将长时间处于 Z 状态。
int main()
{ pid_t id fork(); if(id 0) { int cnt 5; while(cnt) { printf(我是子进程,PID是%d,PPID%d,cnt:%d\n,getpid(),getppid(),cnt); sleep(1); cnt--; } _exit(0); } else {while(1){printf(我是父进程,PID是%d,PPID%d\n,getpid(),getppid());sleep(1);}}return 0;
}上面这段代码在 process 进程中通过调用 fork 接口创建了一个子进程子进程在执行完五次打印后就会被终止掉其中的 exit 函数就是用来终止一个进程父进程将一直运行。 子进程执行完5次打印后就处于 Z 状态并且后面跟了一个单词 defunct该单词有死了的不存在的意思只不过它还再等父进程来回收它的资源。处于 Z 状态的进程的相关资源尤其是 task_struct 结构体不能被释放。只有当父进程把子进程的相关资源回收后子进程才能变成 X死亡状态。我们将这种处于 Z 状态的进程就叫做僵尸进程如果父进程一直不来回收那这种进程会长时间占用内存资源造成内存泄漏。
3.1 僵尸进程危害总结 进程的退出状态必须被维持下去因为它要告诉关心它的进程父进程你交给我的任务我办的怎么样了。可父进程如果一直不读取那子进程就将一直处于 Z 状态。 维护退出状态本身就是要用数据维护也属于进程基本信息所以保存在 PCB 对象中换句话说Z状态一直不退出PCB一直都要维护。 一个父进程如果创建了很多的子进程就是不回收会造成内存资源的浪费因为 PCB 对象本身就要占用内存。 造成内存泄漏。
四、孤儿进程
上面我们是让子进程先退出父进程一直运行接下来我们让父进程先退出子进程一直运行看看会有什么结果。
int main()
{ pid_t id fork(); if(id 0) {//子进程 int cnt 500; while(cnt) { printf(我是子进程,PID是%d,PPID%d,cnt:%d\n,getpid(),getppid(),cnt); sleep(1); cnt--; } _exit(0); } else {//父进程 int cnt 5;//这里的cnt是5意味着父进程会先执行结束 while(cnt--) { printf(我是父进程,PID是%d,PPID%d,cnt:%d\n,getpid(),getppid(),cnt);sleep(1); } } return 0;
}可以看到父进程在执行结束后就只剩下子进程为什么父进程不会处在 Z僵尸状态呢答案是父进程也是 bash 的子进程父进程在执行结束后它的父进程 bash 会将其回收掉并且过程非常快所以我们我们没有看到父进程处在 Z僵尸状态。其次我们发现当父进程结束后它的子进程的父进程会变成1号进程即操作系统。我们将父进程是1号进程的进程叫做孤儿进程该进程被系统领养。因为孤儿进程未来也会退出也要被释放所以它需要被领养。
小Tips所有的进程只对它的“儿子”即子进程负责不会对它的孙子进程负责因为代码中只有创建子进程的逻辑并没有创建孙子进程的逻辑所以并不是不想让爷爷进程来回收孙子进程的资源是因为爷爷进程没有这个本事而操作系统会直接从内核层面进行回收所以当一个进程的父进程结束后会把该进程交给操作系统让操作系统来充当它的父进程。
五、结语
今天的分享到这里就结束啦如果觉得文章还不错的话可以三连支持一下春人的主页还有很多有趣的文章欢迎小伙伴们前去点评您的支持就是春人前进的动力