进程的状态

进程的状态

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的进程就相当与程序本身.

相关推荐
秋天的怀念_David1 小时前
算法一千零一夜 · 第十七夜 长街点灯
java·程序人生·算法
CV爱数码1 小时前
论文精读|RED-CNN:残差编解码网络,低剂量 CT 去噪的经典基线
网络·人工智能·算法·cnn·ct·重建·x-ray
JustNow_Man2 小时前
【Harness】本地文本向量化搜索解决方案--qmd
git·python·算法
敲代码的苦132 小时前
头歌 数据结构—消防资源调度
数据结构·算法
布莱克6052 小时前
C++ 中 set 与 map 的区别详解
开发语言·c++·算法·哈希
lisw052 小时前
算法与软件的本质区别
驱动开发·算法·数学建模
我不会插花弄玉2 小时前
5.Hash 【由浅入深-redis】
redis·算法·哈希算法
Nuanyt2 小时前
LeetCode Hot 100 · 双指针 3 题 Java
java·笔记·算法·leetcode·双指针
志起计算机编程3 小时前
Clickhouse的几个关键优势
算法