前面的文章讲述了进程的一些基本的概念,本文中来介绍进程状态的相关信息。
进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
当进程在CPU上跑的时候,并不是一直在运行的。有多个进程的时候会让每个进程都运行一会,进行切换,由于我们人的感官与CPU运行速度差异较大,所以无法发现。
进程状态查看

先简要的介绍两个状态:
阻塞:
进程因为等待某种条件,而导致的一种不推进的状态 -- 进程卡住了 -- 阻塞一定是在等待某种资源(以平时在浏览器上下载文件为例子,当断网时下载在速度变为0kb,进度条不长,这就是在等待网络资源)-- 为什么阻塞?进程要通过等待的方式,等具体的资源被别人用完之后,再被自己使用。阻塞就是进程等待某种资源(磁盘、显卡、网卡等)就绪的过程。
操作系统管理资源与进程的时候都需要有先描述在组织的形式。因此每个进程都有一个假设为task_struct的结构体对象,各种资源也需要有假设为struct_dev的对象来进行管理。
在内核中可以有如下图的结构,当有一个进程不能再CPU上跑时,此时该进程就会将自己的PCB链接在相关资源的尾部进行排队,等待某种资源,这就被称为进程阻塞。
总结:阻塞就是不被调度。---一定是因为当前进程需要等待某种资源 --- 一定是进程task_struct结构体需要被OS管理的资源下排队。
挂起:
系统资源特别紧张,操作系统就会根据算法将闲置的不被调度的进程放入磁盘中,此时这部分的代码与数据就可以被释放,当进程等待的资源就绪的时候,进程需要再被调度,就可以磁盘中重新放入内存中。
下面就以Linux为例子:
先编写一个简单地程序
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("正在运行!\n");
sleep(1);
}
return 0;
}

使用相关命令可以看到状态为S+(休眠状态)。当我们将while中的代码注释掉再次运行就会发现状态变成了下图的情况:
S+为阻塞状态的一种,因为CPU的访问速度与访问外设的速度相差很大,printf的本质就是外设打印消息,当CPU执行printf代码时,如果需要频繁打印,外设不一定是准备就绪的,因此进程就会在外设上进程排队,等到外设就绪,再进行打印工作,所以在使用命令进行查询的时候很大一部反查到的就是S+状态,有很少的情况会是R+状态。
R运行状态
当while中没有printf语句时,当前代码中没有任何访问资源的代码,是一个纯计算的代码,所以在整个进程调度的生命周期中只会调用CPU资源,因此为R+状态,表示该进程在运行队列中排队。
S休眠状态,可中断休眠
当我们有这样的一段代码:
while(1)
{
int a = 0;
scanf("%d", &a);
printf("%d\n", a);
}
开始运行的时候就会,等待键盘外设,当我们输入之后,进程就可以继续运行。
T停止状态
使用kill -l可以查看相关的指令:
对于一段正在运行的程序,可以通过kill -19 [PID] 来将其停止:

然后可以使用 kill -18 [PID] 指令来继续程序的运行:

在此时继续查看进程的属性,我们就可以看到其状态由S+变成了S。此时,可以发现使用ctrl+c无法使程序停止。

+表示该进程运行在前台,没有+表示进程运行在后台。若是需要将进程终止就可以使用kill -9 [PID]来杀死程序。
t追踪式停止状态
使用之前学习的gdb调试器就可以在进程属性中查看到t状态。

x死亡/z僵尸状态
在c语言的学习中经常会写return 0;这个表示进程的退出码,使用echo $? 就可以查看对应的退出码。

若是程序的结果有问题就会返回不同的进程退出码:
Linux进程退出时,一般进程不会立即彻底退出,而是要维持一个状态叫做z,也叫做僵尸状态 -- 方便后续父进程os读取子进程退出的退出结果。使用下面的代码
pid_t id = fork();
if (id == 0)
{
// 子进程
while (1)
{
printf("本进程是子进程PID:%d\n", getpid());
sleep(2);
}
}
else if (id > 0)
{
// 父进程
while (1)
{
printf("本进程是父进程PID:%d\n", getpid());
sleep(2);
}
}
可以发现当在父子进程同时运行时停止子进程,在查看进程属性就变为了Z+

2035

被折叠的 条评论
为什么被折叠?



