1.基础知识-进程的5个状态
进程可以分为五个状态,分别是:
1)创建状态
一个应用程序从系统上启动,首先就是进入创建状态,需要获取系统资源创建进程管理块(PCB)完成资源分配。
2) 就绪状态
在创建状态完成之后,进程已经准备好,但是还未获得处理器资源,无法运行。
3) 运行状态
获取处理器资源,被系统调度,开始进入运行状态。如果进程的时间片用完了就进入就绪状态。
4) 阻塞状态
在运行状态期间,如果进行了阻塞的操作,如耗时的I/O操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。
5) 终止状态
进程结束或者被系统终止,进入终止状态
进程的状态转换图如下所示:
2.通过linux内核源码深入理解进程状态转变
在Linux内核源码中(include/linux/sched.h)找到进程状态的定义。
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_STATE_MAX 1024
重要的进程状态含义如下表所示:
定义 | 含义 |
TASK_RUNNING | 可执行状态(执行状态、执行等待状态)。可运行状态的进程,是指正在使用CPU 或者正在等待 CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态(Running 或 Runnable)的进程。进程处于可运行状态,但并不意味着进程已经实际上已分配到CPU ,它可能会一直等到调度器选中它。该状态只是确保进程一旦被 CPU 选中时立马可以运行,而无需等待外部事件。(就绪和运行两种状态) |
TASK_INTERRUPTIBLE | 等待状态。等待状态可被信号解除。这是针对等待某事件或其他资源而睡眠的进程设置的。在内核发送信号给该进程时表明等待的事件已经发生或资源已经可用,进程状态变为TASK_RUNNING,此时只要被调度器选中就立即可恢复运行。 |
TASK_UNINTERRUPTIBLE | 等待状态。等待状态不可被信号解除。处于此状态,不能由外部信号唤醒,只能由内核亲自唤醒。 |
TASK_STOPPED | 表示进程特意停止运行。比如在调试程序时,进程被调试器暂停下来。 |
可以通过如下流程图深入理解几个状态的变化:
TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE 的区别
1)TASK_INTERRUPTIBLE(可中断的睡眠状态)
TASK_INTERRUPTIBLE是可以被信号和wake_up()唤醒的,当信号到来时,进程会被设置为可运行。可中断的睡眠状态的进程会睡眠直到某个条件变为真,比如说产生一个硬件中断、释放进程正在等待的系统资源或是传递一个信号都可以是唤醒进程的条件。系统中绝大多数进程处于这个状态。
2)TASK_UNINTERRUPTIBLE(不可中断的睡眠状态)
TASK_UNINTERRUPTIBLE只能被wake_up()唤醒。不可中断睡眠状态与可中断睡眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠状态的进程不能改变它的状态,也就是说它不响应信号的唤醒。不可中断睡眠状态一般较少用到,但在一些特定情况下这种状态还是很有用的,比如说:进程必须等待,不能被中断,直到某个特定的事件发生。
TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。
不可中断状态的进程,可以这么理解:
这个进程现在基本上不在CPU 执行指令,但是它还是占用这个 CPU。
这个进程在做一些事情(大部分是磁盘I/O),做的这个事情不能被打断。
为啥不能被打断,因为系统觉得它做的这个事情很重要,如果打断了可能结果很严重(比如进程正在读写磁盘数据,把它打断了可能会导致进程的数据和磁盘上的数据不一致的情况)。
注:
信号本质:信号是在软件层次上对中断机制的一种模拟
信号事件的发生有两个来源:
硬件来源:(比如我们按下了键盘或者其它硬件故障);
软件来源:最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
3. Linux负载与进程状态的关系
3.1 top命令查看负载
通过top命令可以查看系统负载:
[root@localhost ~]# top
top - 12:26:46 up 1 day, 13:32, 2 users, load average: 0.00, 0.00, 0.00
其中:load average后的三个数字分别代表系统在之前 1 分钟、5 分钟、15 分钟的平均负载。如果 CPU 是单核的,则这个数值超过 1 就是高负载:如果 CPU 是四核的,则这个数值超过 4 就是高负载 。
3.2 Linux平均负载
CPU使用率和平均负载容易混淆,但二者并不等同。
1)定义
平均负载是指单位时间内,系统处于可运行状态和不可中断状态的任务数(CPU、磁盘、不间断锁),它和 CPU使用率没有直接的关系。
CPU使用率表示单位时间CPU的利用情况。
2)CPU使用率和平均负载区别的原因
Linux内核源码中Linux 负载平均数不仅跟踪 runnable 的任务,而且还跟踪处于 uninterruptible sleep 状态的任务。而 uninterruptible 状态的进程其实是不占 CPU 的。所以平均负载(loadaverage)并不代表CPU使用率。
3) CPU使用率和平均负载的关系有三个场景
a)CPU密集型进程,大量使用CPU会使CPU利用率和平均负载都增高。
b) IO密集型进程,会使平均负载增高但CPU使用率不一定会增高。
c) 大量等待CPU的进程调度会使平均负载增高,CPU使用率也会增高。
4) 根据平均负载的数值对系统状态进行判断
如果负载小于CPU核心数,那么大概率是CPU使用导致,如果负载远高于CPU核心数,例如单核CPU,但是负载跑到了几十,那么大概率是磁盘发生问题导致。
参考文章:
https://mp.weixin.qq.com/s/1Pl4tT_Nq-fEZrtRpILiig
https://blog.csdn.net/qq_25854057/article/details/120769493
https://www.cnblogs.com/embedded-linux/p/7043569.html