欢迎来到小林的博客!!
️博客主页:✈️小林爱敲代码
️博客专栏:✈️Linux之路
️社区 :✈️ 进步学堂
️欢迎关注:点赞收藏✍️留言
看看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运行状态(running) | 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。 |
S睡眠状态(sleeping) | 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。 |
D磁盘休眠状态(Disk sleep) | 有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。 |
T停止状态(stopped) | 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。 |
t追踪状态(tracing stop) | 也是一种暂停状态,调试时的进程就是这种状态。 |
X死亡状态(dead) | 这个状态只是一个返回状态,你不会在任务列表里看到这个状态。 |
进程的执行流程如下:
运行状态,顾名思义,就是正在运行的状态。我们写一个一直循环的程序来测试一下。
我们写了一个死循环的程序,而程序什么都不做。
然后编译运行,在另一边来查看该进程。查看进程命令: ps ajx | head -1 && ps ajx | grep "myprocess"
我们可以看到这2个进程都是R+状态,因为刚刚那个程序还创建了一个子进程。+号代表前台,再执行的时候加上&就会变成后台。而R是运行状态的,也就是说这两个程序是前台运行的。
那么我们在执行之后加上 &试试 。 也就是后台执行。
我们可以发现状态变成R了,也就是在后台运行。
而后台和前台的关系就很好理解了。后台的话你可以进行其他操作,前台运行则不行。
后台运行可以执行其他命令。但是后面换成前台运行,就无法执行其他命令了。
而这些运行中,轮流运行的这个队列,叫做运行队列。运行队列的进程状态都是R状态。因为只有R状态才会被操作系统挂到运行队列。
上面说到了运行队列。那么还有等待队列,等待队列里的进程都是处于睡眠状态。
那么进程在什么时候会处于睡眠状态? 当我们完成某种任务时,任务条件不具备,需要进行某种等待。
打个比方,我们C语言写了个程序,程序里面有一个scanf函数。它会一直等待你输入数据进去,那么你一直不输入,是否证明程序一直在运行状态呢?答案是否定的,此时的程序会处于睡眠状态,因为CPU很忙,如果你一直不输入,那么进程一直运行的话,会极大的降低效率。
那我们就来实践一下。我们写一个有scanf的C语言代码。
然后编译执行。
执行之后,程序一直在等我们输入。
我们不输入,我们去查看它的状态。
然后我们随便输入一个值,再重新查看一下状态。
它又重新变成运行状态了,因为陷入在死循环里面。
所以:当睡眠状态的进程,已经具备了任务条件。比如要求你输入,你输入完之后。那么它就会被唤醒,被唤醒之后就会把状态更改成R。随后把这个进程从等待队列,拉进运行队列。
深度休眠状态不会被操作系统强制终止。
磁盘休眠状态就是,打个比方:
你想要从磁盘读取文件,然后你的进程对磁盘说:小磁啊,我想要这个1个G的文件,你拿给我吧!然后磁盘本身就是个打工人,它就照着进程的吩咐去做了。因为磁盘正在为进程读取数据,所以进程此时是休眠状态。这个时候操作系统突然过来,看见进程居然是休眠状态。对进程说,我已经都忙不过来了,你居然还在这里偷懒?操作系统实在气不过,就把进程给干掉了。这时候磁盘读取完了,却不知道这1个G的数据该交给谁??而这种现象也造成了用户的数据丢失,那么这种现象是谁的锅? 说是磁盘的锅吧,但磁盘就是个打工人,你有什么需求,它就按你的吩咐做。你说是进程的锅吧,它又什么都没做,一直在睡眠。你说是操作系统的锅吧,可是操作系统为了效率,把这个占着茅坑不拉屎的进程给干掉了,也没有问题。 那么计算机会允许这种分不清是谁责任的事情出现吗? 当然不会,这时候就有了深度睡眠。 深度睡眠无法被强制终止。
停止状态也可以理解成暂停状态,和休眠状态很像。都可以充当挂起状态。而它和休眠状态的区别就是:
休眠状态虽然处于休眠状态,但是它的一些核心数据可能会被更新。
而T状态就是彻底的暂停,也不会有任何的数据更新。
追踪状态,也是一种暂停。并且是用在正在调试中的程序,想必大家都调试过程序,调试的过程如果你不进行下一步,那么程序是不会继续往后执行的。
死亡状态,顾名思义,就是程序要挂了。我们也无法演示这个状态,因为当程序是x状态的时候,那就意味着它要挂了。就是程序终止前被设置了这种状态,然后程序立马终止掉了。
什么是僵尸进程?举个稍微有点恐怖的例子:
假如有一天,你走在路上,突然有一个人倒在你面前,把你吓了一跳。这时候你打了110或者120,然后警察过来。发现这个人已经死了,那么警察会直接把它拖走吗?答案是当然不会!警察应该第一时间封锁现场,然后请法医鉴定。鉴定出死亡原因…等等其他的东西之后,才会让人把身体挪走。那么我们就可以把这个想象成一个进程,这个进程要挂掉了,那么我们应该立即终止它吗?答案也是当然不能了!因为还没有搞清楚这个进程的终止原因,所以我们要搞明白这个进程终止的原因,才能让进程安心归去对不对?
在这个进程已经挂掉了的时候,操作系统还在"调查"它的死亡原因时。这个阶段,这个进程就是僵尸进程。
那么怎么僵尸进程由谁回收呢?
僵尸进程由它的父进程回收
那么我们可以做个测试,来测一下僵尸进程。我们让父进程什么都不做,那么父进程就无法回收子进程了。这时候强制把子进程杀掉,再来查看一下子进程的信息。
直接让父进程睡眠50s,我们只需要在50s内杀掉子进程即可。
然后我们还要有3个控制台,一个拿来执行程序,一个拿来杀掉子进程,一个拿来查看进程。
我们执行该程序
然后查看进程信息,我们发现都是S休眠状态。
然后,我们把子进程kill掉。再查看一下进程信息。
这时候子进程就变成僵尸进程了。因为父进程还在睡眠,无法处理子进程。
那么还是刚刚那个程序,但是这次,我不杀子进程了,我改成杀父进程。会造成什么样的后果?因为进程之间是相互独立的,即使我把父进程干掉,子进程已经可以还在运行。
话不多说,还是刚刚那个程序。
首先我们运行程序。
然后查看一下进程。
此时的父子进程都是休眠状态。
然后我们杀掉父进程
杀掉父进程后我们再查看进程信息,发现子进程12594的ppid变成了1号进程。而父进程在的时候,ppid是父进程的pid。也就是说,当父进程被kill掉的时候。那么1号进程会"领养"这个父进程的子进程。而此时1号进程也被称为孤儿进程。
孤儿进程就是父进程被kill掉了,然后被1号进程领养的进程。
然后我们杀掉父进程
杀掉父进程后我们再查看进程信息,发现子进程12594的ppid变成了1号进程。而父进程在的时候,ppid是父进程的pid。也就是说,当父进程被kill掉的时候。那么1号进程会"领养"这个父进程的子进程。而此时1号进程也被称为孤儿进程。
孤儿进程就是父进程被kill掉了,然后被1号进程领养的进程。