✨个人主页: Yohifo
🎉所属专栏: Linux学习之旅
🎊每篇一句: 图片来源
🎃操作环境: CentOS 7.6 阿里云远程服务器
Perseverance is not a long race; it is many short races one after another.
- 毅力不是一场漫长的比赛;是许多短跑一个接一个。
文章目录
- 📘前言
- 📘正文
- 📖阻塞
- 📖挂起
- 📖进程状态
- 🖋️运行 R
- 🖋️睡眠 S
- 🖋️休眠 D
- 🖋️暂停 T
- 🖋️死亡 X
- 🖋️僵尸 Z
- 📘总结
📘前言
进程
只有被OS管理好了,才能发挥它的全部功效,而系统中存在多个 进程
,OS无法做到面面俱到,因此为了更好的管理进程
,OS把 进程
分成了几种状态:阻塞、挂起、运行、休眠等等,至于每种状态的应用场景是什么、有什么用?本文将会带着大家认识的各种 进程
状态
📘正文
在谈 进程状态
之前,首先要回顾下之前的 进程
相关知识
- OS管理的本质是先描述,再组织
- OS并非直接管理
进程
,而是管理进程
的PCB(task_struct)
PCB
中有着进程的各种信息,包括:PID
、PPID
、进程状态
等- 我们可以通过函数
getpid()
获取当前进程的PID
进程
间存在父子关系,可以通过fork()
主动创建子进程
父子进程
相互独立,共享一份代码时,具有写时拷贝
机制
📖阻塞
何为阻塞?
阻塞
就是进程
因等待某种条件就绪,而导致的一种不推进状态- 通俗来说,
阻塞
就是进程
卡住了,原因就是缺少资源
比如在我们日常生活中,常常发生堵车,原因就是道路资源不够用了,车辆这个 进程
就需要原地等待
那么进程
需要什么资源呢?
- 比如
磁盘
、网卡
、显卡
等各种外设 - 假设你现在想在
steam
上下载游戏,当你点击下载按钮后提示磁盘空间不足,此时是无法运行steam下载
这个进程的,因为此进程
需要等待足够大的磁盘资源
- 此时我们就称此
进程
为阻塞
状态
总结
- 进程阻塞就是不被调度
原因
- 进程的 task_struct 结构体需要在某种被 OS 管理的资源下排队
📖挂起
理解 进程阻塞
后,理解 进程挂起
就比较轻松了
挂起(阻塞挂起)
- 当
CPU
资源紧张时,将进程
交换至磁盘
中挂起,此时内存中只有PCB
挂起可以看作一种特殊的阻塞状态
比如在我们生活中,一边走路一边玩手机很危险,所以此时我们会将玩手机这个 进程挂起
,即把手机揣进兜里,然后 专心执行走路这个 进程
📖进程状态
进程
有各种运行状态,方便OS进行管理,在 Windows
中,进程
状态是这样的
而在我们 Linux
中,新建
、就绪
、运行
都可以看作 运行 R
这一个状态,所以比较清晰
而我们今天要学习的正是 Linux
中的 进程
状态
进程是何种状态,取决于此进程的PCB在哪里排队
🖋️运行 R
首先来看看第一种状态 R
以我们以往的认知来说,一个程序在运行就表示该 进程
处于 运行
状态,那么事实真的如此吗?
先来看看下面这段代码:
#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
int main()
{
while(1)
{
cout << "I'm a process, my PID is:" << getpid() << endl;
sleep(1);
}
return 0;
}
当前 Makefile
文件为
myProcess:test.cpp
g++ -o myProcess test.cpp
.PHONY:catPI
catPI:
ps ajx | head -1 && ps ajx | grep myProcess | grep -v grep
.PHONY:clean
clean:
rm -r myProcess
通过 make catPI
指令调用 Makefile
中提前设定好的指令,查看当前进程信息
可以看到当前的进程状态为 睡眠 S+
注: +
表示当前进程在前台运行中
进程
难道没有运行吗?
- 运行了,但我们
很难捕捉到
- 对于 CPU 来说,将这么简单的一句话输出到屏幕上是一件很小的事,可能几毫秒就完成了
- 而其他大多数时间,
进程
都在外设等待队列中排队
- 当我们将打印语句和睡眠语句屏蔽后,
进程
不用在等待队列中排队
, CPU 就一直在处理死循环,此时可以观察到运行 R
状态
此时进程 myProcess
就在运行中
注意: R
表示此时 进程
已经在 运行队列
中排队了,但 进程
不一定在 CPU 上运行
🖋️睡眠 S
睡眠 S
的本质就是 进程阻塞
,表示此时进程因等待某种资源而暂停运行;睡眠 S
又称为可中断休眠,当 进程
等待时间过长时,我们可以手动将其关闭,应用卡死后强制关闭也是这个道理
还有一种方式终止进程:kill
kill -9 PID
终止进程,当进程在后台运行时(状态不加+
),我们是无法通过ctrl+c
终止的,但kill
指令可以终止
🖋️休眠 D
还存在一种特殊睡眠状态 休眠 D
,休眠
又被称为不可中断休眠,顾名思义,休眠 D
状态下的 进程
是无法终止的,kill
指令和 OS都无能为力,只能默默等待 进程阻塞
结束,拿到资源了,进程
才会停止 休眠 D
状态
终止 休眠 D
进程的一个方法就是切断电源,此时进程是结束了,但整个系统也结束了
倘若存在 休眠 D 进程长时间运行,那么此时就表示系统离宕机不远了
不可休眠状态比较少见,一般出现于IO阻塞
用途:
- 使操作系统无法杀死该
进程
,预防误杀现象
🖋️暂停 T
我们还可以使 进程
进入 暂停 T
状态
kill -19 PID
暂停进程kill -18 PID
恢复进程
我们可以通过 kill -18 PID
使 进程
恢复运行,恢复后的 进程
在后台运行
注意: 进程
在后台运行时,是无法通过 ctrl+c
指令终止的,只能通过 kill -9 PID
终止
在 gdb
中调试代码时,打断点实际上就是 使 进程
在指定行暂停运行,此时 进程
处于 追踪暂停状态 t
🖋️死亡 X
当进程被终止后,就处于 死亡 X
状态
死亡状态是无法在任务列表中观察到的,死亡 X
状态只是一个返回状态
🖋️僵尸 Z
与死亡状态相对应的还有一个 僵尸 T
状态
- 通俗来说,
僵尸状态
是给父进程
准备的 - 当
子进程
被终止后,会先维持一个僵尸
状态,方便父进程
来读取到子进程
的退出结果,然后再将子进程
回收 - 单纯的在
bash
环境下终止子进程
,是观察不到僵尸状态
的,因为bash
会执行回收机制,将僵尸
回收 - 我们可以利用
fork()
函数自己创建父子进程
关系,观察到这一现象
#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t ret = fork();
if(ret == 0)
{
while(1)
{
cout << "I'm son process, my PID: " << getpid() << " PPID: " << getppid() << endl;
sleep(1);
}
}
else if(ret > 0)
{
while(1)
{
cout << "I'm father process, my PID: " << getpid() << " PPID: " << getppid() << endl;
sleep(1);
}
}
else
{
while(1)
{
cout << "Make son process fail!" << endl;
sleep(1);
}
}
return 0;
}
此时输入指令 kill -9 PID
即 kill -9 28315
终止 子进程
再次查看进程状态:
僵尸进程如果不被回收,会导致内存泄漏问题和标识符占用问题
关于 僵尸进程
的更多信息,将会在下篇文章中介绍
📘总结
以上就是关于进程学习【二】的全部内容了,我们简单学习了 进程
的相关状态,知道了何为 阻塞
、进程
为什么 阻塞
的缘由,同时还见识了 进程
的各种状态,为以后 进程
进一步学习和控制打下了基础
如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!
如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正
…
相关文章推荐
Linux进程学习【一】
Linux工具学习之【gdb】
Linux工具学习之【git】
Linux工具学习之【gcc/g++】
Linux工具学习之【vim】
Linux 权限理解和学习