【Linux】 进程(4)七大进程状态深度解析


Linux 七大进程状态深度解析

内核源码中的状态定义

进程状态存储在 task_structstateexit_state 字段中,定义于 include/linux/sched.h

复制代码
struct task_struct {
    volatile long state;       /* 运行中状态:-1 不可运行, 0 可运行, >0 停止 */
    int exit_state;            /* 退出中状态:EXIT_ZOMBIE / EXIT_DEAD */
    // ...
};

具体状态标志:

复制代码
/* tsk->state 使用 */
#define TASK_RUNNING            0x0000
#define TASK_INTERRUPTIBLE      0x0001
#define TASK_UNINTERRUPTIBLE    0x0002
#define TASK_STOPPED            0x0004
#define TASK_TRACED             0x0008

/* tsk->exit_state 使用 */
#define EXIT_ZOMBIE             0x0010
#define EXIT_DEAD               0x0020

关键区分:state 字段管理进程"活着"时的状态(R/S/D/T/t),

exit_state 字段管理进程"退出过程中"的状态(Z/X)。

两者互斥使用。


一、R --- TASK_RUNNING(运行态 / 就绪态)

定义

复制代码
#define TASK_RUNNING  0x0000

值为 0,是所有状态中唯一表示"可以被调度执行"的状态。

语义拆解

TASK_RUNNING 实际上包含了传统操作系统中的两个子状态:

子状态 含义
运行中(Running) 进程正在 CPU 上执行指令
就绪(Ready) 进程已准备好,在运行队列(runqueue)中等待被调度

Linux 不区分这两者,统一用 TASK_RUNNING 表示。

区别仅在于:该进程的 task_struct 是否正在被某个 CPU 的 current 指针指向。

进入 R 态的触发条件

  • 进程被 fork 创建后,初始即为 TASK_RUNNING
  • 睡眠进程等待的事件发生,被 wake_up() 唤醒
  • 停止态进程收到 SIGCONT 信号
  • 时间片耗尽后被调度器换下 CPU,但仍保持 R 态(在就绪队列中)

离开 R 态的触发条件

  • 主动调用 schedule() 放弃 CPU(如等待某资源)
  • 时间片耗尽,被调度器抢占(但仍在 R 态就绪队列中,不算离开)
  • 收到停止信号进入 T 态
  • 调用 exit() 退出

运行队列与调度

所有处于 TASK_RUNNING 的进程都挂在每 CPU 运行队列(struct rq)上。

CFS 调度器从红黑树中挑选 vruntime 最小的进程执行。

复制代码
CPU 0 的运行队列(红黑树,按 vruntime 排序):

        [P3: vruntime=100]
       /                  \
[P1: vruntime=80]    [P5: vruntime=150]
                          /
                   [P2: vruntime=120]

调度器选择最左侧节点(vruntime 最小)→ P1 上 CPU 执行

实际场景

复制代码
# 查看正在运行的进程
ps aux | awk '$8 ~ /R/'

一个 CPU 密集型程序(如死循环计算)会长期处于 R 态:

复制代码
while (1) {
    // 纯计算,不调用任何阻塞函数
    result += compute_something();
}

二、S --- TASK_INTERRUPTIBLE(可中断睡眠态)

定义

复制代码
#define TASK_INTERRUPTIBLE  0x0001

语义

进程在等待某个事件发生,主动放弃 CPU 进入睡眠。

但它可以被信号唤醒------当一个信号递达时,进程会被唤醒去处理信号,即使等待的事件尚未发生。

进入 S 态的典型场景

场景 内核操作
sleep(10) 定时器未到期前睡眠
read() 阻塞终端/网络 等待数据到达
wait() / waitpid() 等待子进程退出
pause() 等待任意信号
等待互斥锁/信号量 锁被其他进程持有
select() / poll() / epoll_wait() 等待 I/O 事件

内核中的睡眠机制

进程进入可中断睡眠的标准模式:

复制代码
// 定义等待队列节点
DEFINE_WAIT(wait);

// 将自己加入等待队列,并设置状态
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);

if (!condition) {
    schedule();  // 主动调度,放弃 CPU
}

// 被唤醒后,从等待队列移除
finish_wait(&wq, &wait);

唤醒路径有两条:

  1. 事件发生:其他进程/中断调用 wake_up(&wq),遍历等待队列唤醒进程
  2. 信号递达:内核在信号处理路径中检查进程状态,如果是 TASK_INTERRUPTIBLE,则唤醒进程

被信号唤醒后的行为

进程被信号唤醒后,通常会检查是否有信号待处理。如果有,系统调用(如 read)会返回 -EINTR,表示"被信号中断":

复制代码
ssize_t ret = read(fd, buf, size);
if (ret == -1 && errno == EINTR) {
    // 被信号中断,可以选择重试
    continue;
}

这就是为什么很多健壮的代码会在循环中重试被 EINTR 中断的系统调用。

实际场景

绝大多数后台服务进程在空闲时都处于 S 态------它们在等待客户端请求、等待 I/O 完成。

复制代码
# 统计 S 态进程数量(通常占绝大多数)
ps aux | awk '$8 ~ /^S/' | wc -l

三、D --- TASK_UNINTERRUPTIBLE(不可中断睡眠态)

定义

复制代码
#define TASK_UNINTERRUPTIBLE  0x0002

语义

进程在等待不可中断的事件,通常是底层 I/O 操作(磁盘读写、NFS 挂载等)。

与 S 态的核心区别:不能被信号唤醒,只能由等待的事件完成来唤醒

为什么需要不可中断?

这是最常被问到的面试点。假设进程正在执行磁盘 read

  1. 进程通过系统调用进入内核态
  2. 内核向磁盘控制器发出 I/O 请求
  3. 磁盘正在机械地寻道、读写(毫秒级延迟)
  4. 进程需要等待 I/O 完成

如果此时允许信号中断这个进程:

  • 进程可能在 I/O 完成前就退出或处理信号
  • 内核态的 I/O 操作可能处于不一致状态
  • 已发出的磁盘请求无法干净地回滚
  • 可能导致数据损坏或内核崩溃

因此,在 I/O 操作的关键路径上,进程必须设为不可中断睡眠,确保 I/O 完成前进程不会被干扰。

进入 D 态的典型场景

场景 说明
磁盘 read / write 等待物理 I/O 完成
fsync / fdatasync 等待数据刷入磁盘
NFS 网络文件系统操作 等待网络 I/O,网络不通时可能长时间 D 态
直接 I/O(O_DIRECT 绕过页缓存,直接等待磁盘
某些设备驱动操作 如磁带机、原始设备访问

D 态与系统负载

D 态进程会被计入系统负载(load average)。

这是一个重要的知识点:

Linux 的 load average 不仅统计 R 态进程,还统计 D 态进程。

因此,当系统 load 很高但 CPU 使用率很低时,往往意味着大量进程处于 D 态------磁盘 I/O 是瓶颈。

复制代码
# 查看 D 态进程
ps aux | awk '$8 ~ /D/'

# 用 iostat 确认磁盘瓶颈
iostat -x 1

D 态能被杀死吗?

**不能。 因为 D 态进程不响应信号,kill -9(SIGKILL)也无法终止它。**进程会一直保持 D 态,直到:

  1. 等待的 I/O 操作完成
  2. I/O 超时(某些驱动有超时机制)
  3. 系统重启

这也是 D 态进程令人头疼的原因------如果磁盘或 NFS 出现故障,D 态进程可能永远卡在那里,唯一的解决办法是修复 I/O 子系统或重启。

内核中的 io_schedule()

进入不可中断睡眠通常使用 io_schedule()

复制代码
// 设置为不可中断睡眠并调度
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
io_schedule();  // 内部调用 schedule(),并统计 iowait 时间

io_schedule() 与普通 schedule() 的区别在于:它会将等待时间计入 iowait/proc/stat 中的 iowait 字段),这也是 top%wa 的来源。


四、T --- TASK_STOPPED(停止态)

定义

复制代码
#define TASK_STOPPED  0x0004

语义

进程被暂停执行,既不在 CPU 上运行,也不在等待任何事件。它完全"冻结",直到收到恢复信号

进入 T 态的方式

方式 说明
SIGSTOP 信号 不可被捕获/忽略/阻塞,强制停止进程
SIGTSTP 信号 终端 Ctrl+Z 发送,可被捕获
SIGTTIN 信号 后台进程尝试读取终端时
SIGTTOU 信号 后台进程尝试写入终端时
ptrace 附加 调试器附加时,目标进程进入停止态

从 T 态恢复

只有一个方式:收到 SIGCONT 信号。

复制代码
# 停止一个进程
kill -STOP 1234

# 查看状态(应为 T)
ps -o pid,stat,cmd -p 1234

# 恢复进程
kill -CONT 1234

终端作业控制中的 T 态

Ctrl+Z 是最常见的触发场景:

复制代码
$ sleep 100
# 按下 Ctrl+Z
^Z
[1]+  Stopped                 sleep 100

$ jobs
[1]+  Stopped                 sleep 100

$ bg %1        # 后台继续运行(发送 SIGCONT)
[1]+ sleep 100 &

$ fg %1        # 前台恢复运行
sleep 100

T 态进程的资源

处于 T 态的进程:

  • 保留所有内存资源(不会被换出,但可以被回收)
  • 保留文件描述符
  • 不在运行队列中,不会被调度
  • 不响应除 SIGCONT 外的大多数信号(但信号会被记录为挂起)

注意:SIGKILL 可以终止 T 态进程吗?

可以。SIGKILL 对 T 态进程有效,进程会直接进入退出流程。但其他常规信号(如 SIGTERM)在 T 态下不会被立即处理,要等进程恢复到 R/S 态后才会递达。


五、t --- TASK_TRACED(追踪态)

定义

复制代码
#define TASK_TRACED  0x0008

语义

进程被调试器追踪时的暂停状态。当 gdb 等调试器通过 ptrace 附加到进程,并命中断点或单步执行时,目标进程进入 TASK_TRACED

与 T 态(STOPPED)的区别

这是一个高频面试考点:

对比项 T(STOPPED) t(TRACED)
触发方式 SIGSTOP / SIGTSTP / Ctrl+Z ptrace 断点 / 单步
恢复方式 SIGCONT 信号 调试器调用 ptrace(PTRACE_CONT)
能否被 SIGCONT 唤醒 不能(必须由 ptrace 恢复)
信号处理 挂起信号等待恢复 调试器可拦截信号
ps 显示 T t(小写)

ptrace 与 TRACED 态

ptrace 是 Linux 提供的进程追踪机制,gdbstraceltrace 等工具都基于它。

复制代码
// 父进程(调试器)追踪子进程
pid_t child = fork();
if (child == 0) {
    // 子进程:允许被追踪
    ptrace(PTRACE_TRACEME, 0, NULL, NULL);
    execl("/bin/ls", "ls", NULL);
} else {
    // 父进程:等待子进程事件
    int status;
    waitpid(child, &status, 0);
    
    // 设置断点、单步执行等
    ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
    // 子进程执行一条指令后进入 TASK_TRACED
}

当子进程命中断点时,内核会发送 SIGTRAP 信号,子进程进入 TASK_TRACED,父进程通过 waitpid 获知。

实际场景

复制代码
# 用 gdb 调试一个程序
gdb ./myprogram
(gdb) break main
(gdb) run

# 另一个终端查看
ps aux | grep myprogram
# wxx  7800  0.0  0.1  ...  t  10:30  0:00 ./myprogram
#                                    ↑ 小写 t,表示 TRACED 态

六、Z --- EXIT_ZOMBIE(僵尸态)

定义

复制代码
#define EXIT_ZOMBIE  0x0010

注意这是定义在 exit_state 字段中,而非 state 字段。

语义

进程已经执行完毕,释放了用户态资源(内存、文件描述符等),但内核中的**task_struct 尚未释放****。它像一具"尸体"一样留在进程表中,等待父进程来"收尸"。**

僵尸进程的产生流程

复制代码
1. 子进程调用 exit() 或从 main 返回
   ↓
2. 内核释放子进程的用户态资源(地址空间、文件描述符、信号处理等)
   ↓
3. 子进程的 exit_state 设为 EXIT_ZOMBIE
   ↓
4. 内核向父进程发送 SIGCHLD 信号
   ↓
5. 子进程保留 task_struct(包含退出码、资源使用统计等信息)
   ↓
6. 父进程调用 wait()/waitpid() 读取子进程退出信息
   ↓
7. 内核释放子进程的 task_struct → 进入 EXIT_DEAD

如果步骤 6 从未发生,子进程就永远停留在僵尸态。

为什么需要僵尸态?

这是面试高频问题。僵尸态存在的意义是:

保存子进程的退出状态和资源使用信息,供父进程查询。

父进程可能想知道:

  • 子进程是正常退出还是被信号杀死?
  • 退出码是多少?
  • 子进程累计使用了多少 CPU 时间?
  • 子进程的最大内存驻留集是多少?

这些信息存储在 task_struct 中,必须等父进程通过 wait() 读取后才能释放。如果子进程一退出就完全销毁,父进程就无法获取这些信息了。

代码演示

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程:立刻退出
        printf("【子进程】PID=%d,即将退出\n", getpid());
        exit(42);  // 退出码 42
    } else {
        // 父进程:不调用 wait,睡眠 60 秒
        printf("【父进程】PID=%d,子进程 PID=%d,不回收,睡眠 60 秒\n",
               getpid(), pid);
        sleep(60);
    }

    return 0;
}

运行后另开终端:

复制代码
ps aux | grep -E 'Z|defunct'
# wxx  8101  0.0  0.0  0  0 pts/0  Z  10:35  0:00 [zombie_demo] <defunct>

注意僵尸进程的 VSZRSS 都是 0------用户态内存已释放,只剩内核数据结构。

僵尸进程的危害

危害 说明
占用 PID 每个僵尸进程占用一个 PID 号,系统 PID 有限(默认 32768)
占用内核内存 每个 task_struct 约 1.7KB,数量多时不可忽视
无法直接杀死 kill 对僵尸进程无效,因为它已经"死了"
影响进程创建 PID 耗尽后,fork 会失败返回 EAGAIN

如何处理僵尸进程

方法一:父进程调用 wait() / waitpid()

复制代码
int status;
waitpid(pid, &status, 0);  // 阻塞等待指定子进程
// 或
wait(&status);               // 等待任意子进程

方法二:SIGCHLD 信号处理(非阻塞回收)

复制代码
void sigchld_handler(int sig) {
    int saved_errno = errno;
    // WNOHANG:不阻塞,没有已退出的子进程则立即返回
    while (waitpid(-1, NULL, WNOHANG) > 0);
    errno = saved_errno;
}

int main() {
    struct sigaction sa;
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sigaction(SIGCHLD, &sa, NULL);
    // ...
}

为什么用 while 循环?

因为多个子进程可能同时退出,而信号不排队,一次 SIGCHLD 可能对应多个子进程退出。


方法三:父进程先退出,让 init 收养

如果父进程先于子进程退出,子进程会被 init(PID 1)收养。init 有一个专门的 SIGCHLD 处理函数,会自动回收所有孤儿进程,不会产生僵尸。


方法四:两次 fork(高级技巧)

复制代码
pid_t pid = fork();
if (pid == 0) {
    // 第一个子进程
    pid_t grandchild = fork();
    if (grandchild == 0) {
        // 孙子进程:执行实际任务
        execvp(...);
    }
    // 第一个子进程立即退出,孙子进程被 init 收养
    exit(0);
}
// 父进程等待第一个子进程(很快退出)
waitpid(pid, NULL, 0);

孙子进程的父进程(第一个子进程)立即退出,孙子被 init 收养,自动回收,父进程也无需长期等待。


如何"杀死"一个已经存在的僵尸进程

僵尸进程无法被直接杀死,因为它已经退出了。唯一的方法是:

  1. 杀死它的父进程:父进程死后,僵尸进程被 init 收养,init 会自动回收

  2. 让父进程调用 wait:如果父进程还在运行,可以通过调试器等方式让它执行 wait

  3. 重启系统:终极手段

    找到僵尸进程的父进程 PID

    ps -o ppid= -p <zombie_pid>

    杀死父进程(谨慎!)

    kill


七、X --- EXIT_DEAD(死亡态)

定义

复制代码
#define EXIT_DEAD  0x0020

同样定义在 exit_state 字段中。

语义

进程的最终状态。父进程已经通过 wait() 回收了子进程的退出信息,内核即将释放 task_struct。这个状态极其短暂------通常只持续几个指令周期,ps 命令几乎不可能捕捉到。

进入 X 态的流程

复制代码
父进程调用 waitpid()
    ↓
内核调用 release_task()
    ↓
将子进程 exit_state 设为 EXIT_DEAD
    ↓
从进程表中移除(PID 可重用)
    ↓
释放 task_struct 内核栈
    ↓
释放其他剩余内核资源
    ↓
进程彻底消失

为什么需要 X 态?

既然 Z 态之后进程就要被释放了,为什么还要一个中间的 X 态?

原因是并发安全。在多 CPU 系统中,释放 task_struct 的过程不是原子的。

设置 EXIT_DEAD 标志可以:

  1. 告知其他 CPU:这个进程正在被释放,不要访问它的 task_struct

  2. 作为 release_task() 中的状态检查,防止重复释放

  3. 在释放过程中保持一个稳定的状态标识

    // kernel/exit.c 中的 release_task()
    void release_task(struct task_struct *p)
    {
    // ...
    p->exit_state = EXIT_DEAD; // 标记为死亡态
    // ... 执行释放操作
    call_rcu(&p->rcu, delayed_put_task_struct); // 延迟释放
    }

X 态的可见性

复制代码
# 几乎不可能看到 X 态进程
ps aux | awk '$8 ~ /X/'
# 通常无输出

如果你真的看到了 X 态进程,那可能是:

  • 系统处于极高负载,释放操作被延迟
  • 内核存在 bug
  • 你在 ps 输出刷新的瞬间恰好捕捉到了

状态转换全景图

复制代码
                              fork()
  新建 ───────────────────────────→ R (TASK_RUNNING)
                                        │
                    ┌───────────────────┼───────────────────┐
                    │                   │                   │
              等待事件/资源         时间片到              收到停止信号
              (可中断)             (仍在就绪队列)        (SIGSTOP/Ctrl+Z)
                    │                   │                   │
                    ▼                   ▼                   ▼
              S (可中断睡眠)          R                  T (停止态)
              D (不可中断睡眠)                             │
                    │                                       │
              事件完成/I/O完成                           SIGCONT
              (D态只能被事件唤醒)                           │
              (S态还可被信号唤醒)                            │
                    │                                       │
                    └───────────────────┬───────────────────┘
                                        │
                                        ▼
                                        R
                                        │
                   ┌────────────────────┼────────────────────┐
                   │                    │                    │
              ptrace断点             exit()/信号          正常执行
                   │                    │                    │
                   ▼                    ▼                    │
              t (追踪态)          Z (僵尸态)                 │
                   │                    │                    │
              ptrace CONT          父进程 wait()             │
                   │                    │                    │
                   └────────────────────┼────────────────────┘
                                        │
                                        ▼
                                  X (死亡态)
                                        │
                                        ▼
                                  资源释放,消失

补充知识

1. 进程状态与 load average 的关系

Linux 的 load average 统计的是处于不可中断睡眠(D)和运行/就绪(R)状态的进程数的指数衰减平均值。

复制代码
load average = 正在运行的进程(R) + 等待I/O的进程(D)

这解释了一个经典问题:为什么 CPU 使用率很低,但 load average 很高?

→ 因为大量进程在 D 态等待磁盘 I/O,它们被计入 load 但不消耗 CPU。


2. ps 中 STAT 列的附加字符

除了基本状态字符,ps 的 STAT 列还可能包含附加修饰符:

字符 含义
s 会话首进程(session leader),通常是 shell
l 多线程进程
+ 位于前台进程组
< 高优先级(nice 值为负)
N 低优先级(nice 值为正)
L 有页面锁定在内存中(mlock)

例如 Ssl+ = 可中断睡眠 + 会话首进程 + 多线程 + 前台进程组。


3. 内核线程的状态

内核线程(如 kthreaddkworkermigration)也使用相同的状态机。你在 ps 中看到的 [kworker/0:0] 这类带方括号的进程就是内核线程。它们通常处于 S 态(等待工作)或 R 态(处理工作)。


4. 空闲进程的状态

每个 CPU 都有一个空闲进程(idle task,PID 0),当没有其他 R 态进程时运行。空闲进程不占用进程表项,也不在 ps 中显示。


高频面试题

Q1:Linux 有哪几种进程状态?分别是什么含义?

参考答案:

  • R(Running):运行或就绪,在 CPU 上执行或在运行队列等待调度
  • S(Sleeping):可中断睡眠,等待事件,可被信号唤醒
  • D(Disk sleep):不可中断睡眠,通常等待 I/O,不能被信号唤醒
  • T(Stopped):停止态,被 SIGSTOP 等信号暂停,SIGCONT 恢复
  • t(Traced):追踪态,被调试器暂停,由 ptrace 恢复
  • Z(Zombie):僵尸态,进程已退出但父进程未回收,保留 task_struct
  • X(Dead):死亡态,父进程已回收,task_struct 即将释放

Q2:D 态和 S 态有什么区别?为什么需要 D 态?

参考答案:

  • S 态可被信号唤醒,D 态不能被信号唤醒
  • D 态用于保护内核态关键操作(主要是 I/O)的完整性。如果在磁盘 I/O 过程中允许信号中断,可能导致 I/O 操作处于不一致状态,引发数据损坏
  • D 态进程会被计入 load average,S 态不会
  • kill -9 无法杀死 D 态进程

Q3:僵尸进程是怎么产生的?如何避免和处理?

参考答案:

  • 产生:子进程退出后,父进程未调用 wait()/waitpid() 回收,子进程的 task_struct 残留在进程表中
  • 避免:
    1. 父进程显式调用 wait()waitpid()
    2. 注册 SIGCHLD 信号处理函数,在其中用 waitpid(-1, NULL, WNOHANG) 循环回收
    3. 父进程先退出,子进程被 init 收养自动回收
    4. 两次 fork,让孙子进程被 init 收养
  • 处理已存在的僵尸:杀死其父进程,让 init 收养并回收

Q4:T 态和 t 态有什么区别?

参考答案:

  • T 态由 SIGSTOP/SIGTSTP/Ctrl+Z 触发,可被 SIGCONT 恢复
  • t 态由 ptrace 调试触发(断点、单步),只能由调试器通过 ptrace(PTRACE_CONT) 恢复,SIGCONT 无效
  • T 态进程的信号会被挂起,t 态进程的信号可被调试器拦截和修改

Q5:为什么 load average 高但 CPU 使用率低?

参考答案:

  • load average 统计的是 R 态 + D 态进程数
  • D 态进程在等待 I/O(通常是磁盘),不消耗 CPU 但计入 load
  • 这种情况说明系统的瓶颈在磁盘 I/O,而非 CPU
  • 可用 iostat -xiotop 排查磁盘瓶颈,用 ps aux | awk '$8 ~ /D/' 查看 D 态进程

Q6:kill -9 能杀死哪些状态的进程?不能杀死哪些?

参考答案:

  • 能杀死:R、S、T、t、Z(僵尸进程本就已死,kill 无意义但不报错)
  • 不能杀死:D 态(不可中断睡眠,不响应任何信号,包括 SIGKILL)
  • X 态瞬间即逝,不存在杀死的问题

Q7:fork 之后子进程的初始状态是什么?

参考答案:

  • TASK_RUNNING。子进程被创建后直接加入运行队列,等待被调度执行。
  • 但子进程是否立即运行取决于调度器,父子进程的执行顺序不确定。

Q8:进程退出的完整流程是什么?

参考答案:

  1. 进程调用 exit()(或从 main 返回,编译器自动插入 exit)
  2. 内核执行 do_exit():关闭文件描述符、释放地址空间、释放信号处理等用户态资源
  3. 设置 exit_state = EXIT_ZOMBIE,向父进程发送 SIGCHLD
  4. 保留 task_struct(含退出码、资源统计)
  5. 父进程调用 wait()/waitpid(),内核执行 release_task()
  6. 设置 exit_state = EXIT_DEAD,释放 task_struct 和内核栈
  7. 进程彻底消失,PID 可被重用

总结

Linux 的七大进程状态构成了一个完整的生命周期状态机:

  • R 是进程"活着且能动"的状态
  • S/D 是进程"睡着等事件"的状态,区别在于能否被信号打断
  • T/t 是进程"被暂停"的状态,区别于是被信号还是调试器暂停
  • Z/X 是进程"已死"的状态,区别在于是否已被父进程回收

理解这些状态不仅能帮助你写出更健壮的代码(正确处理 EINTR、避免僵尸进程),更是排查线上问题(load 高、进程卡死、系统响应慢)的必备知识。在面试中,进程状态也是操作系统和 Linux 系统编程方向的高频考点。


相关推荐
HiDev_1 小时前
【非标自动化】2、认识元器件(气源处理组件)
运维·自动化
HiDev_1 小时前
【非标自动化】2、认识元器件(远程I/O)
运维·自动化
国际云,接待1 小时前
df 显示磁盘还有空间却无法写文件:Linux inode 耗尽排查与治理
linux·运维·服务器
青少儿编程课堂1 小时前
用图形化编程做一个“少年探险闯关”小游戏:方向键控制、碰撞检测与多关卡串起完整项目
c++·python·算法·bfs·信息学竞赛
HiDev_2 小时前
【非标自动化】2、认识元器件(运动控制模块)
运维·自动化
CQU_JIAKE2 小时前
8.22【A】
算法
一路向北finish2 小时前
《CentOS 7 搭建 Hadoop 3.3.6 分布式集群:NFS 共享方案完整实操》
linux·运维·hadoop·分布式·centos
raindayinrain2 小时前
深入理解Linux内核--缺页异常,写时复制
linux·写时复制·缺页异常
xiaoxiangsiyan2 小时前
全网IPv6规模化改造实战指南
运维·网络·笔记·云原生·自动化