目录
回顾
上篇文章Linux-进程的介绍和标示符,讲了进程是由进程控制模块(PCB)、它的代码和数据构成的,而在Linux中进程的PCB就是一个名叫task_struct的结构体,里面存放着进程的数据和状态,操作系统如果要对进程进行操控,是对PCB进行管理,而不是对进程代码数据直接管理。其中我们上篇文章详细讲了PCB存放的标示符(PID)和它的父进程标示符(PPID)。
而这篇文章,我们详细讲解一下进程的状态。
进程的状态
我们先来看看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 */
};
1."R"状态
R(running),顾名思义,就是运行状态,一个程序在cpu中被跑的时候,就是运行状态。
这里我们提出一个疑问,我们打开一个app,打开一个播放器,打开qq,打开网页,我们随时可以对这些程序进行操作,是不是代表着这些进程无时无刻处于运行状态呢? 不是的
一般我们的电脑都只带了一个cpu,可是,有那么多进程,cpu是怎么“同时运行”那么多进程的呢?
如果说cpu是运行完一个进程再继续运行下一个进程,就会导致一个进程可能会长时间占用我们的cpu,而导致其他进程没有享受到cpu的资源。所以cpu是通过一个运行队列来完成“同时运行”多个进程的。
而这里就又提出了一个时间片的概念,一个进程在队列的一个轮回中被运行的时间。
一个进程运行了对应的时间片时间,立马就到队列尾部,而后一个进程就再被cpu运行对应的时间片时间,以此反复,从而达到“同时运行”的效果,而这个“同时运行“并不是真正的同时运行,只是我们肉眼看到的“同时运行”,因为周期很快所以我们感受不到。
而一个程序对应的时间片也是存放在PCB中,影响进程对应的时间片时间是一个进程的优先级,我们后面会讲。
我们写一串代码感受一下
看右边我们的进程有一个R+的状态,这就说明它是在运行中。
1."S"状态
R(sleeping),睡眠状态,说明当前程序不在运行队列,可能是因为某些设定或者在等待某些硬件的输入输出。
跟R状态一样,它也有一个队列,名为rest_queue
我们直接来感受一下
让这个程序每隔一秒运行一次。
这里我们就能看到程序大部分时间是处于S状态,程序在R状态存在的时间太短暂了!!
这种情况就是程序因为设定,会从运行队列移动到等待队列中。
而另一种情况
让这个程序不间断的打印内容。是不是会一直处于运行状态呢?
我们连续多次查看进程状态,发现这个进程大部分时间是处于“S”状态的,只有一小部分时候处于“R”状态,这是为什么呢?我们明明看到程序一直在打印刷屏。
这是因为我们的输入输出设备比较慢,而我们的cpu更快,我们的程序已经准备就绪了,可是还需要等待输入输出设备的响应,这就导致了我们的进程进入等待队列中等待输入输出设备的响应,这就是第二种情况。
3.“D”状态
D磁盘休眠状态(disk sleep) 称为深度睡眠状态,不可中断睡眠状态。
如果一个进程长时间没有等到对应的数据触发运行条件,操作系统会认为这个进程是一个垃圾进程,会把这个进程杀掉,可是,有的时候一个程序就是需要等那么久,又怕被操作系统误杀,那怎么办,这时候进程就有一种特殊的睡眠状态,就是不让操作系统随便把进程杀掉,等到IO数据全部准备就绪再运行,这就叫做深度睡眠也叫不可中断睡眠。
4.“T”状态
T停止状态(stopped)可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
这个状态与休眠,状态有一定区别,进程即使处于S状态,它里面的PCB数据也有可能会因为其他原因发生改变,但是停止状态真的是停止了,并且如果我们要想让一个程序进入T状态,就必须通过发送SIGSTOP信号。
我们使用 kill -l 命令来查看有几种信号模式
kill -9 命令是发送杀死进程的信号
kill -19 命令是发送停止进程的信号
kill -18 命令是发送进程继续的信号
我们使用刚刚的死循环程序,让他一直跑下去
进程停止后,我们再查看进程的状态
这时他就处于“T”状态,不知道大家是否发现没有,之前我们查看进程的状态都有“R+”,“S+”,
可是这里的“T”没有“+”,这是什么意思?
“+”的意义
如果一个进程的状态后面有+,说明这个进程是在前台运行的,什么是前台运行?就比如我们可以通过ctrl + c 中断程序,但是如果是后台运行,我们就不能通过这种ctrl+c的方式中断程序,并且后台打印的内容不影响我们在前台输入指令。
这里我们紧接上面的已经处于“T”状态的进程,如果要让他继续跑起来使用 kill -18 [进程PID]
让他重新跑起来之后,进程的状态也变成的没有+,并且我没办法中断程序,那么我又应该怎么然他停下来?
使用kill -9 [进程PID]命令
程序终于停下来了
5.“t”状态
"t (tracing stop)", 可以称为调试停止状态
我们在调试程序的时候就是处于这种状态
我们使用gdb进入调试
6.“X”状态
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
例如一个程序跑完了,会返回一个值。
7.“Z”状态
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
举个例子
这个程序,子进程会很快走完,父进程会一直走下去,子进程的返回值本应该给父进程,但是如果父进程没有走完,子进程已经走完了,子进程的返回值就谁都给不了,就会处于僵尸状态。
注意:如果一个进程处于僵尸状态,那么这个进程将无法被杀死,即使使用kill -9也无济于事,这就可能会导致一个问题——内存泄漏。
这就是“Z”僵尸状态。
8.孤儿进程
那假如子进程走完之后,父进程已经跑完了,或者是被杀掉了,该怎么办?
父进程只打印一次并让他休眠10s,子进程一直在运行。
我们会看到,当父进程跑完之后,子进程的PPID变成了1,而PID为1的进程就是我们的操作系统,所以,可以得出结论,如果父进程跑完了,子进程还在跑,子进程就会变成孤儿进程,被操作系统接手,并且运行状态变成了后台运行。
总结
以上我们就详细的介绍了进程的所有的状态,而进程的状态信息都是储存在进程的PCB中,操作系统通过从PCB查看进程的状态来分配进程。