樊梓慕:个人主页
个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》
每一个不曾起舞的日子,都是对生命的辜负
目录
前言
1.什么是状态?
1.1运行
1.2阻塞
1.3阻塞挂起
2.Linux下具体的进程状态
2.1运行状态-R
2.2可中断睡眠状态-浅度睡眠状态-S
2.3不可中断睡眠状态-深度睡眠状态-D
2.4停止状态-T
2.5死亡状态-X
2.6僵尸状态-Z
2.6.1僵尸进程的危害
2.7孤儿进程
本篇文章博主将会讲解进程状态,之前我们讲到进程是有生命的,所以他在整个生命期间必然会有状态的变化。那么具体而言什么是状态呢?Linux中包含有那些状态呢?
欢迎大家收藏以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。
=========================================================================
GITEE相关代码:fanfei_c的仓库
=========================================================================
所谓的状态其实就是task_struct中的一个整型变量。
状态决定了什么?你的后续动作!而Linux中可能会存在多个进程都要根据它的状态执行后续动作,此时就需要排队了。
一个CPU,一个运行队列,也就是说这些进程都需要在CPU这里进行排队,逐个执行。
而排队的是进程么?
其实不是,排队的是进程的PCB(面试时不是你在排队,而是你的简历信息在排队)。
在PCB中存在着多个节点成员,这些节点成员的存在就是为了“先描述再组织”中的组织而存在的。这也就实现了一个task_struct可以被链入多种数据结构中从而进程可以在不改变原有队列的同时,实现在各种各样的软硬件资源提供的队列中排队的操作。
但是就算进程放在了CPU上,也不是一直会运行的。在操作系统管理进程有时间片的概念,一个CPU永远不可能真正地同时运行多个任务。在只考虑一个CPU的情况下,这些进程“看起来像”同时运行的,实则是轮番穿插地运行,由于时间片通常很短(在Linux上为5ms-800ms),用户不会感觉到。
就好比我们的电脑,某个进程卡死了,而其他进程照常运行的原因就是这。
当进程在等待软硬件资源的时候(比如等待键盘输入),资源如果没有就绪。
此时task_struct就会被设置为阻塞状态,并链入等待的资源提供的等待队列。
没错,这里的等待队列类似于CPU运行队列。
比如:
状态的变迁,引起的是PCB会被操作系统变迁到不同的队列当中。
挂起状态的前提是计算机资源已经比较吃紧了。
阻塞挂起:因为等待某种软硬件资源就绪,进程对应PCB由运行队列转至资源下的等待队列时,考虑到内存空间紧张,操作系统会将因为等待而暂时无法运行的进程对应的代码和数据先由内存转移到磁盘中,此时进程即为挂起状态,等到该进程可以被运行时再将对应的代码和数据由磁盘转移回内存中。
当计算机资源比较吃紧时,操作系统一定要确保自身不会因为资源的紧张而崩溃,所以就会将一些等待资源(阻塞)的进程的代码和数据交换到磁盘的swap分区中,这个过程称为唤出。
当需要调度此进程时,就会将磁盘的swap分区中保存的内容换回到内存中,这个过程称为唤入。
注意:交换的是进程的代码和数据,不是PCB!!如果PCB被交换出内存了,那操作系统如何管理呢?
所以当某个进程的代码和数据不在内存中,而被换出到磁盘上时,该进程就为挂起状态。
以上的挂起都为阻塞挂起状态。
思考:swap分区是越大越好么?
磁盘本质是输入输出设备,每次唤入唤出其实都是非常低效的操作,如果swap分区设置的过大,那么操作系统就会十分依赖它,导致出现更多低效IO,这本身就是一种牺牲效率来确保操作系统能够正常运行的行为。
所以swap分区不宜设置的过大,一般为内存大小或内存大小的一半,具体要看不同的操作系统。
首先我们来看一下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 *task_state_array[] = {
"R (running)", /* 0*/
"S (sleeping)", /* 1*/
"D (disk sleep)", /* 2*/
"T (stopped)", /* 4*/
"t (tracing stop)", /* 8*/
"Z (zombie)", /* 16*/
"X (dead)" /* 32*/
};
接下来我们逐个介绍以上状态。
运行状态(running)并不意味着进程一定处于运行当中,运行状态表明一个进程要么在运行中,要么在运行队列里。
浅度睡眠状态(sleeping)意味着该进程正在等待某件事情的完成,处于浅度睡眠状态的进程随时可以被唤醒,也可以被杀掉(这里的睡眠有时候也可叫做可中断睡眠(interruptible sleep))。
等同于状态概念中的阻塞状态。
我们来写段代码测试下:
首先我们讲程序运行起来,查看一下进程的状态:
为什么是睡眠状态呢?
处于S+状态时,该进程可用ctrl+c结束。
"+"代表是前台运行,无"+"代表后台运行,后台运行时可在命令行继续输入指令并执行,但无法用ctrl+c结束,需要用kill -9 pid杀死。想要后台运行某个程序就在后面加"&",如:./test &
深度睡眠状态/不可中断睡眠状态/磁盘休眠状态,顾名思义,在这个状态的进程不会被杀掉,哪怕是操作系统也不行,通常会等待IO的结束。
例如,某一进程要求对磁盘进行写入操作,那么在磁盘进行写入期间,该进程就处于深度睡眠状态,是不会被杀掉的,因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答。
如果在这个过程中,操作系统能够杀死该进程,那么就有可能丢失数据。
该状态也属于阻塞状态。
Linux中,操作系统为了防止一些进程做某种危险操作,可能会将进程设置为暂停状态。
我们可以利用SIGSTOP信号使进程进入暂停状态(stopped),发送SIGCONT信号可以让处于暂停状态的进程继续运行。
该状态也属于阻塞状态。
死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,所以你不会在任务列表当中看到死亡状态,死亡状态是一个瞬时过程,我们很难查看到。
僵尸状态就是在等待退出信息被读取时所处的状态。
在这一状态的进程我们称之为僵尸进程。
利用代码进行观察:
为什么要有僵尸状态?
孤儿进程与僵尸进程的情况刚好相反,当子进程的父进程先于子进程退出,那么将来子进程进入僵尸状态时就没有父进程对其进行处理,此时该子进程就称之为孤儿进程。
当出现孤儿进程时,该进程会被1号进程领养(可以认为是操作系统),此后当孤儿进程成为僵尸进程时就由1号进程对其进行读取。
=========================================================================
如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容
博主很需要大家的支持,你的支持是我创作的不竭动力
~ 点赞收藏+关注 ~
=========================================================================