进程的状态

进程的状态

Linux内核源代码:

c 复制代码
/*
*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),在这个状态的进程通常会等待I0的结束。

T停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止(T)进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。

X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

我们可以在Xshell里进行验证

c 复制代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>


int main()
{
     
    pid_t id=fork();
    if(id==0)
    {
        //child
        int count=10;
        while(count)
        {
            printf("我是一个子进程, 我正在运行:->%d, 运行第%\n",getpid(),count);
            sleep(2);
            count--;
        }
    }
    else
    {
        //father
        while(1)
        {
            printf("我是父进程, 我正在运行:->%d\n",getpid());
            sleep(2);
        }
    }
    return 0;
}

运行后观察到子进程的状态是Z(僵尸进程).

另还有我们运行时会发现程序的运行状态是S,而不是R, 造成这样的原因也是CPU运行的太快了.

以上面的代码为例子, 每次循环sleep(2)会休眠两秒,这时候程序在等待队列, 也就是处于休眠状态, 2秒后继续执行, 执行循环也是几纳秒的事情. ps的指令又是一瞬间的就执行的, 所以很难捕捉到是R运行的状态.
还有代码中的printf打印到终端或文件,和一些像write写入scanf输入等函数, 也是由于CPU速度太快, CPU处理的循环的速度是(每秒几百万行)而写入到终端的是(每秒几万行).

这样其实CPU运行时间很短, 虽然看上去程序还在打印内容, 其实是CPU已经处理好的内容, 所以在查看状态时就会显示在休眠状态.

另外补充一下

如果想查看程序进行的R状态, 可以在循环中持续进行计算.

c 复制代码
int i=0;
while(1)
{
	i++;
}

对于T状态, 在程序运行时, 按ctrl+Z就是作业控制停止.

或者是使用kill -19 PID进行停止

如果要恢复程序就是kill -CONT PID

对于t状态可以使用gdb调试观察到.

僵尸进程的危害

单个僵尸几乎无害,危害在于"堆积"。

  1. 占着进程表项不放

    僵尸的 PCB(task_struct 等内核结构)不会被释放,PID 也一直被占用。它不占 CPU、不占用户内存,只占内核里这一小块"户口"。

  2. 耗尽 PID,导致无法创建新进程

    PID 是有限资源(pid_max 默认几万)。僵尸攒多了,fork() 会失败并报 EAGAIN,表现为服务起不来、命令执行不了。容器里 pids cgroup 限额更小,更容易被撑爆。

  3. 积累内核内存

    每个僵尸仍占几 KB 内核内存,成千上万个就是几十 MB,且这部分内存无法回收。

  4. 杀不掉,排查和清理都麻烦

    kill -9 对它无效,只能找到父进程修代码或杀父进程,运维上很被动;监控里进程数、状态统计也会被污染。

  5. 往往是代码缺陷的信号

    出现僵尸说明父进程没正确处理 SIGCHLD / 没调 wait,同一处代码通常还伴随 fd、内存等资源泄漏,值得顺着查。

孤儿进程

孤儿进程是"父进程先走了、自己还活着"的进程。

  1. 怎么产生

    父进程先退出(正常结束或被 kill),子进程还在跑,它就成了孤儿。父进程可能是主动退出,也可能是崩了。

  2. 不会自生自灭,会被"收养"

    内核会把孤儿的父进程改成 init(PID 1)或最近的一个 subreaper(Linux 3.4+ 的 PR_SET_CHILD_SUBREAPER;容器里常是 tini、dumb-init)。所以它的 PPID 会变成 1 或 subreaper 的 PID。

  3. 基本无害

    孤儿进程照常运行,状态是正常的 R/S,照常占 CPU 和内存,也能被 kill。它只是失去了父进程,不是异常状态。

  4. 会被收养者负责回收

    等它退出时,init/subreaper 会调用 wait 收尸,所以正常不会变成僵尸。这正是"孤儿"和"僵尸"的根本区别:

c 复制代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>


int main()
{
     
    pid_t id=fork();
    if(id==0)
    {
        //child
        
        while(1)
        {
            printf("我是一个子进程, 我正在运行:->%d \n",getpid());
            sleep(2);
        }
    }
    else
    {
        //father
        //
        int cnt=10;
        while(cnt)
        {
            printf("我是父进程, 我正在运行:->%d\n",getpid());
            sleep(2);
            cnt--;
        }
    }
    return 0;
}

这里的孤儿进程的父进程将会被PPID为1的进程领养, 这个PPID为1的进程就相当与程序本身.

相关推荐
jyOverQ1 小时前
从零学 JVM(六):垃圾回收算法详解——标记清除、标记复制与标记整理
jvm·算法
严肃小白兔3 小时前
第 06/24 期:神经网络的反向传播算法:从链式法则到梯度计算
人工智能·神经网络·算法
落羽的落羽4 小时前
【C++】C++17的主要特性
linux·服务器·开发语言·c++·算法·机器学习
【心态好不摆烂】4 小时前
数据结构基础精讲3
数据结构·算法
漂流瓶jz5 小时前
UVA-1613 K度图的着色 题解答案代码 算法竞赛入门经典第二版
算法·深度优先·图论·贪心·aoapc·算法竞赛入门经典·uva
Pniubi6 小时前
动态规划入门 ( - 力扣509斐波那契数)
算法·动态规划
吴长建先生重名了7 小时前
GitHub Actions部署报表系统踩坑记:幂等性与限
算法
贝壳6908 小时前
一个2026年2月才上传的库,凭什么敢碰2009年就有人写烂的题目
算法
贝壳6908 小时前
同一道三亿位选型题,三个AI给了我三个答案,我把它们拉进同一台机器复跑
算法