郑州品牌网站建设网上营销
Linux进程状态
- 前言
- 阻塞
- 挂起
- Linux进程状态
- R运行状态
- S睡眠状态
- D磁盘休眠状态
- T停止状态
- X死亡状态
- Z僵尸状态
- 僵尸进程的总结
前言
在介绍Linux的进程状态之前,我们先做一个小调查:
正在运行的程序是一直在运行吗?或者说正在运行的程序一直在被cpu处理吗?
答案是:当然不是!!
正在运行的程序并不是一直在被cpu执行的,如果cpu是把某个程序执行完在执行下一个程序的话,效率是非常低的!同时我们也无法完成边听歌边打游戏的操作!cpu采用的是分时的操作,就是说对于每一个程序cpu都会只执行一小段时间,到时间了就切换去执行下一个程序,由于cpu的切换时间太快了,我们人类是无法感受出来的,这就会造成我们感官上认为多个程序在同一时间运行!但是实际上cpu还是一个一个的执行程序的!
如果我们在内存中加载多个程序的话,cpu在多个程序之间就会调度不过来,在我们用户层面就会发现程序会变得卡顿!
同时再具体介绍Linux进程状态之前,我们先来介绍两个概念:阻塞和挂起;
阻塞
概念:进程因为等待某种条件就绪,而导致一种不推进的状态;
这个概念很抽象,简单来说就是进程卡住了;
那么进程为什么要阻塞呢?
阻塞一定是为了等待某种资源;换而言之进程要等待别的进程使用完资源后,自己再去使用;
因此我们可以重新定义一下阻塞的概念:
阻塞: 进程为了等待某种资源就绪的过程;
我们下面举个具体的例子,来帮助我们理解阻塞:
就比如此时有个cpu和进程:
cpu正在执行该进程的指令时,发现该进程需要向屏幕上输出某些资源,cpu就对进程说:“进程啊,现在你需要使用屏幕资源,但是你又没有,你先去向屏幕申请一下,等你拿到了屏幕资源时,再来找我”,进程答应了,于是cpu就去处理下一个进程了,那么这个进程是如何去申请屏幕资源的?
我们知道os是款搞管理的软件,os自然也能管理:网卡、屏幕、键盘等硬件资源,但是os并不是直接管理这些硬件的,在Linux中OS 先描述 出每个硬件基本信息,并且将这些基本信息放入一个结构体中:
然后再利用某种数据结构将这些结构体再组织起来(假设使用的是链表这种数据结构),那么OS对于这些硬件的管理是不是就是对链表的增删查改,这不就很好的管理起硬件资源了;
既然硬件很好的管理起来了,那么这些硬件是如何知道那些进程在向它申请空间的?
实际上在存储硬件信息的结构体中,还有一个队列:
这个队列就专门用来记录需要申请该硬件资源的进程的pcb(并不是存储进程的代码数据,而是pcb,因为进程也是由OS管理的,OS的管理是不直接管理被管理对象本身的,而是将进程的信息抽象在pcb结构体中,os通过管理pcb来达到管理对应进程);
比如说:现在我的进程需要申请使用网卡资源,那么在存储网卡信息的结构体中的队列就会存储该进程的pcb;如果进程需要申请屏幕资源,那么在存储屏幕信息的结构体中的队列,就会存储该对象的pcb;
整个内存中有许多进程,可能不止我这一个进程需要申请网卡资源,因此我们的进程在申请网卡资源的时候需要在申请网卡资源的队列中进行等待,此时进程并没有被cpu调度,这个在网卡资源队列中等待资源的行为就是阻塞! 当这个等待的进程等到需要的资源时,该进程的pcb就会加载进cpu队列中,等待cpu的处理!
挂起
挂起状态可以说是一个特殊的阻塞状态;
我们具体用一个例子来说明:现在我们的进程需要使用网卡资源,cpu就会让该进程去先申请好了网卡资源,再来被cpu执行,于是该进程就加入了网卡资源的队列中,等待网卡资源的就绪,此时进程就是阻塞状态,但是此时内存空间有点不足了,OS在路过的时候看到我们的进程还在等待网卡资源,就会将我们进程在内存中的代码数据暂时转移到磁盘中去,以此来缓解此时的内存压力,虽然该进程的代码数据被转移到磁盘中去了,但是OS并不会销毁该进程对应的pcb,因此OS还是能管理到该进程的;此时进程处于的这种状态就叫做挂起状态;
当该进程分配网卡资源时,OS又会将该进程的代码数据从外存调回内存;
Linux进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:
/*
* 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运行状态
R运行状态:并不一定表示进程正在被cpu处理,位于cpu的运行队列中的进程也可以被称为处于运行状态;
S睡眠状态
S睡眠状态:进程为了等待某种资源,而处于等待队列当中,实际上就是阻塞状态也被称为浅睡眠状态或可中断睡眠,该状态下的进程可以被杀掉!
我们可以写一断程序来观察S状态:
我们可以利用:ps axj或者ps aux 来查看当前进程的运行状态;
我们可以发现程序确实是存在于S状态,因为cpu在处理到printf语句时,需要该进程提供屏幕资源,该进程就会离开cpu去屏幕资源的等待队列中等待屏幕资源的分配,为此我们能看到进程处于S状态,该状态下的进程是可以被杀掉的!我们利用ctrl+c或者kill -9 pid来杀掉进程;
我们在上面的时候有提到进程是会被cpu执行的,那么我们为什么利用ps -axj命令显示出来的状态是S呢?而不是R?主要是因为cpu的处理速度非常快,当我们的进程把屏幕资源准备好过后cpu瞬间就完成了printf指令,然后又来到了printf,进程又要去屏幕资源的队列中等待分配屏幕资源,等待是需要花时间的,而这个等待时间与cpu处理时间比起来简直大了不知多少倍,就这么说在1s的时间中99.9%的时间花费在了等待资源分配的情况下,实际被cpu处理的时间不到0.1%;因此我们利用ps查看到的进程大多数时间都是处于S状态,极少数时间处于R状态,也就是说理论上我们还是可以捕获到进程处于R状态的,只不过成功几率很低!
除了这种拼手速作法捕获R状态,那么有没有什么办法让我们的进程处于R状态的几率增大呢?
当然可以,我们只要不在程序中申请资源,比如上面的进程是因为要申请屏幕资源,我们直接屏蔽掉printf就好了:
D磁盘休眠状态
D磁盘休眠状态:就是在阻塞状态的基础上,该进程不能被杀掉!只能等待该进程分配到资源,然后进程自然醒来或者重启计算机;不然处于D状态的进程会一直等待资源分配,我们利用kill命令也无法将其杀死,因此D状态也被称为不可中断状态一般情况下我们的计算机都不会出现这情况,一旦有进程处于这个状态,那么说明我们的计算机离宕机不远了!
T停止状态
T停止状态:可以利用kill命令向进程发送SIGSTOP信号来暂停进程;向进程发送SIGCONT信号让进程继续运行;
下面我们通过这个程序来观察T状态:
我们也可以发送继续的信号,让进程继续运行:
我们可以发现经过暂停过后的进程在恢复原状态时状态后面没有了+号;
那么这个+号表示什么呢?
如果一个进程的状态后面还跟了个+号,那么表明这个进程是个前台进程,我们此时无法输入Shell命令,但是我们可以通过Ctrl+c来杀掉该进程或者利用命令kill -9 pid;
如果一个进程的状态后面没有+号,表明这个进程是后台进程,此时我们可以正常输入Shell命令,但是在后台该进程也在运行!此时我们输入Ctrl+c就没有什么作用了,只能通过kill -9 pid的方式来杀掉该进程;
我们在调试程序的时候,如果我们不打断点,直接一个f5编译器就会跑完整段代码,但是当我们在中间打了断点过后,再按f5程序就会在断点处停下来;本质上打断点就是让进程处于暂停状态!
我们可以调试刚才的代码来测试:
t与T一样也是暂停状态!
X死亡状态
X死亡状态:这个状态只是一个返回状态,你不会在任务列表里看到这个状态;
Z僵尸状态
Z僵尸状态:进程一般情况下并不会直接被杀掉,因为当子进程完成它的任务过后,需要向父进程报告它的工作完成的怎么样,而子进程是通过返回进程退出码来告诉父进程的,如果父进程接受了该码,子进程才会被释放!如果父进程没有接受子进程的进程退出码那么子进程就会一直占用着内存资源,得不到释放,此时子进程的这种状态就是僵尸状态;
僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
下面我们通过一段代码来演示僵尸进程:
主要是因为在父进程中,我们并没有接受子进程的进程退出码!导致子进程无法正常释放空间!
僵尸进程的总结
1、僵尸进程存在的意义就是告诉父进程你交给我的任务,我子进程工作的怎么样,如果父进程一直不读取子进程的进程退出码的话,子进程就会变成僵尸进程;
2、维护状态本身也是需要存储数据的,也属于进程的基本信息,所以也保存在pcb中,如果Z状态一直不退出,pcb一直都需要维护;
3、如果一个父进程创建了很多子进程,但是父进程就是不回收子进程,那么在内存中就会存在大量僵尸进程,这些僵尸进程的代码数据也是需要存储的,也是需要消耗大量内存的,如果我们一直不回收,僵尸进程就会一直占这这些内存,会造成内存泄漏!