目录
二.进程状态
2.1 内核源码中的定义
2.2 进程状态查看
2.3 详解各种进程状态
1.R运行状态
2.S休眠状态(阻塞状态)
3.D深度睡眠状态
4.T暂停状态
5.x终止状态
2.4 Z僵尸状态(zombie)
2.5 孤儿进程
二.进程状态
2.1 内核源码中的定义
进程在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 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
2.2 进程状态查看
使用:
ps aux / ps axj 命令
选项 | 含义 |
-a | 显示终端上的所有进程,包括其他用户的进程 |
-u | 显示进程的详细状态 |
-x | 显示没有控制终端的进程 |
-j | 列出与作业控制相关的信息 |
此时我们在LInux下输入ps axj 后,系统便会弹出当前各种进程的信息
在这里面我们会看到第一行列出了许多参数,下面简单介绍几个,其中:
- PPID(Process ID)--- 父进程ID
- PID(Parent Process ID)--- 进程ID
- PGID(Process Group ID)--- 进程组ID
- SID(Session ID)--- 会话ID
- UID(User ID) --- 用户id
- STAT:里面的参数对应2.1上表,就是该进程此时的状态
2.3 详解各种进程状态
1.R运行状态
task_struct 结构体在运行队列中排队,就叫做运行态
(就好比你在食堂排队,同学打电话问你在干嘛,我们一般会回答在吃饭)
我们用while随便写一个死循环,再编译运行,此时这个进程的状态就可以称作R状态
2.S休眠状态(阻塞状态)
系统中存在着各种资源,例如CPU,网卡,磁盘,显卡......等多种设备.
也就决定了系统中存在多种队列,各个进程想要访问同一硬件资源就必须得排队
我们称等待CPU资源就位的进程为运行状态,那么称等待非CPU资源就绪的状态就称为阻塞状态
就比如我们常常写的scanf代码, 就是在等待键盘输入的就绪,此时就可以称为S状态
3.D深度睡眠状态
当服务器压力过大的时候,操作系统会通过一定的手段,杀掉一些进程,来起到节省空间的作用!
假设此时存在一个进程正在向磁盘写入数据,但如果此时服务器压力过大,我们希望杀掉这个进程吗?
当然不会!我们肯定不希望重要的数据丢失,这时候我们的系统的就会选择将该进程的状态设置为D状态
此时该状态下的进程无法被系统杀掉,同时也不能被动唤醒,只能等该进程得到磁盘的返回结果(写入成功或失败)后主动醒来,如果计算机存在大量D状态的进程甚至可能无法关机T
4.T暂停状态
想让一个程序处于暂停状态我们可以使用kill命令
这里我们用kill -l 列出了kill命令可以发送的所有信号,其中的 18 与 19 就与暂停状态有关
- kill -19 PID :让PID对应的进程处于停止状态
- kill -18 PID :让PID对应的处于停止状态的进程恢复运行
应用场景:
当我们在调试代码时,打断点让程序停下来,本质上就是调试器向进程发送信号,让其处于停止状态
5.x终止状态
进程在执行结束后,会短暂进入x状态,等待操作系统回收
实际上由于计算机的高速运转,资源回收非常迅速,该状态瞬时性非常强,一般很难捕捉
2.4 Z僵尸状态(zombie)
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面在写到进程控制时会详细讲解) 没有读取到子进程退出的返回代码时就会产生僵尸进程
- 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
来一个创建维持30秒的僵死进程例子:
编译代码得到程序
此时再编写shell脚本来监控进程
之后我们在运行我们的程序后
此时再观察编写的监控程序,便可以看到我们新创建的进程
5s后,子进程结束,而父进程还在,且没有读取子进程状态,则子进程成为僵尸进程,状态变为Z
僵尸进程危害:
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了,可父进程如果一直不读取,那子进程就一直处于Z状态
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护
- 那一个父进程创建了很多子进程,就是不回收会造成内存资源的浪费,因为数据结构对象本身就要占用内存(想想C中定义一个结构体变量,是要在内存的某个位置进行开辟空间)
- 最终一定会造成内存泄漏
2.5 孤儿进程
- 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
- 父进程先退出,子进程就称之为“孤儿进程”
- 孤儿进程会被1号init进程自动领养,还要由init进程回收,孤儿进程实际上没有什么危害
这里大家可以类比僵尸进程的代码,自行编写观察