目录
TASK_STOPPED 状态和 TASK_TRACED 状态
进程的状态
进程是无法始终占有 CPU 资源的,原因是:
- 进程可能需要等待某种外部条件的满足,在条件满足之前,进程是无法继续执行的。这种情况下,该进程继续占有 CPU 就是对 CPU 资源的浪费
- Linux 是多用户多任务的操作系统,可能同时存在多个可以运行的进程,进程的个数可能远远多于 CPU 的个数。一个进程始终占有 CPU 对其他进程来说是不公平的,进程调度器会在合适的时机,选择合适的进程使用 CPU
- Linux 进程支持软实时,实时进程的优先级高于普通进程,实时进程进程之间也有优先级的差别。软实时进程进入可运行状态的时候,可能会发生抢占,抢占当前运行的进程
Linux 下传统的进程 7 状态:前一项时 procfs 文件系统中的值,后一项是进程状态
- R(running) TASK_RUNNING
- S(sleeping) TASK_INTERRUPTIBLE
- D(disk sleep) TASK_UNINTERRUPTIBLE
- T(stopped) TASK_STOPPED
- t(tracing stop) TASK_TRACED
- Z(zombie) TASK_ZOMBIE
- X(dead) TASK_DEAD
可运行状态
该状态的名称为 TASK_RUNNING。有人说 Linux 进程有八种状态,这种说法也是对的。因为 TASK_RUNNING 状态可以根据是否在 CPU 上运行,进一步细分成 RUNNING 和 READY 两种状态。处于 READY 状态的进程,随时可以投入运行,只是由于 CPU 资源有限,调度器暂时未被选中它运行。
处于可运行状态的进程是进程调度的对象。如果进程并不处于可运行状态,进程调度器不会选择它投入运行。在 Linux 中每一个 CPU 都有自己的运行队列,事实上还不止一个,根据进程调度类别的不同,可运行状态的进程也会位于不同的队列中:如果是实时进程(属于事实调度类),则根据优先级的情况,落在相应的优先级队列中;如果是普通进程(属于完全公平调度类),则根据虚拟运行时间的大小,落在红黑树的相应结点上。这样进程调度器可以根据一定的算法从运行队列上挑选合适的进程来使用 CPU 资源。
处于 RUNNING 状态的进程,可能正在执行用户态代码,也可能正在执行内核态代码,内核提供了进一步的区分和统计。Linux 提供的 time 命令可以统计进程在用户态和内核态消耗的 CPU 时间:
time 命令统计了三个时间:
- 实际时间 :日常生活中的时间,即进程从开始到结束一共执行了多久
- 用户 CPU 时间 :进程执行用户态代码消耗的 CPU 时间
- 系统 CPU 时间 :进程在内核态运行所消耗的 CPU 时间
如何区分用户态 CPU 时间和内核态 CPU 时间呢?如果进程在执行加减乘除或排序等操作时,尽管这些操作正在消耗 CPU 资源,但是和内核并没有太多的关系,CPU 大部分时间都在执行用户态指令。这种场景下我们称 CPU 时间消耗在用户态。如果进程频繁的执行进程创建、进程销毁、分配内存、操作文件等,那么进程不得不频繁的陷入内核执行系统调用,这些时间都累加在进程的内核态 CPU 时间中。
在单核系统上 real time 总是不小于 user time 和 sys time 的总和。但是在多核系统上,user time 和 sys time 的总和可以大于 real time。利用这三个时间我们可以算出程序的 CPU 使用率:
cpu_usage = (user time + sys time) / real time
在多核处理器的情况下,如果 cpu_usage 大于 1,则表示该进程是计算密集型的进程,且 cpu_usage 的值越大,表示越充分的利用了多处理器的并行运行优势;如果 cpu_usage 的值小于 1,则表示进成为 I/O 密集型的进程,多核并行的优势并不明显。
time 命令的问题在于要等进程运行完毕后,才能获取到进程的统计信息。有些时候我们需要了解正在运行的进程,他运行了多久、用户态 CPU 时间和内核态 CPU 时间分别是多少?procfc文件系统在 /proc/PID/stat 中提供了相关信息:
每个字段都有自己独特的含义。如果从 0 开始计数,那么字段 13 对应的是进程消耗的用户态 CPU 时间,字段 14 对应的是进程消耗的内核态 CPU 时间。两者的单位是始终滴答。
系统提供了 pidstat 命令,通过该命令可以获取到各个进程的 CPU 使用情况:
pidstat 命令可以通过 -p 参数指定观察的进程,从而获取到该进程的 CPU 使用时间,包括用户态 CPU 时间和内核态 CPU 时间。