文章目录一、Linux进程五大基本状态1. 运行状态RRunning / Runnable2. 可中断睡眠状态SInterruptible Sleep3. 不可中断睡眠状态DUninterruptible Sleep4. 停止状态TStopped / Traced5. 僵死状态ZZombie / Defunct二、D 状态和Z状态排查1.D 状态排查方法1. wchan stack2. hung_task2. Z 状态排查方法3.防止Z状态产生一、Linux进程五大基本状态在 Linux 系统中常用的五大基本状态通过 ps、top 等命令可查看分别是1. 运行状态RRunning / Runnable含义进程当前正在运行占用 CPU或者处于可运行队列中、只要获得 CPU 时间片就能立刻执行。特点包括正在 CPU 上执行的进程以及就绪等待调度的进程。这是进程争取 CPU 时的活跃状态。示例一个不停计算的 while(1) 程序通常处于 R 状态在多核系统上多个 R 状态进程可能同时运行。2. 可中断睡眠状态SInterruptible Sleep含义进程正在等待某个事件完成如等待 I/O 输入、等待锁、等待信号等并且该睡眠可以被信号唤醒。特点最常见的睡眠状态大部分时间系统进程都在此状态。当等待的资源可用或收到信号时进程会回到 R 状态。可以被 kill 命令或其它信号打断。示例Shell 等待用户输入、网络服务等待客户端连接、进程调用 sleep() 或 nanosleep()。3. 不可中断睡眠状态DUninterruptible Sleep含义进程正在等待某些不可被中断的 I/O 操作如直接读写磁盘、等待硬件响应期间无法响应任何信号。特点通常用于磁盘 I/O、某些设备驱动中的关键操作。即使发送 SIGKILL 也无法立即终止该进程必须等待 I/O 完成或系统重启。这种设计是为了防止在关键数据写回磁盘时被中断导致数据不一致。示例sync 命令刷新缓冲区、dd 直接写入块设备、NFS 因网络故障僵住时的等待操作。注意短时间的 D 状态是正常的若长时间存在且数量增多往往提示 I/O 瓶颈或存储设备问题。4. 停止状态TStopped / Traced含义进程的执行被暂停通常是由于收到了 SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU 等信号或者被调试器如 gdb暂时接管。特点进程不会获得 CPU 时间也不响应普通信号除 SIGKILL、SIGCONT 外。可以通过发送 SIGCONT 信号让进程恢复到 R 状态继续执行。示例在终端按下 CtrlZ 将前台进程挂起 或 gdb 中设置断点后命中暂停 或 使用 kill -SIGSTOP 显式暂停。5. 僵死状态ZZombie / Defunct含义进程已经结束运行调用了 exit 或收到终止信号但其进程描述符task_struct仍然保留等待父进程调用 wait() 或 waitpid() 来读取其退出状态。特点该进程不再占用任何内存或 CPU 资源只保留内核中的一个极小结构体。无法被杀死因为它已经“死”了只能通过让父进程回收它来消除。若父进程不回收且不退出僵尸进程会一直存在若父进程先退出僵尸进程会被 initPID1进程收养并自动回收。示例父进程编写不当未调用 wait导致子进程结束后一直处于 Z 状态。大量僵尸进程可能耗尽进程号上限影响系统运行。本文将具体介绍D状态和Z状态的排查与处理。二、D 状态和Z状态排查1.D 状态排查方法1. wchan stackwchanwait channel能看到 D 状态进程“卡”在内核的哪个具体函数例如ps-eopid,state,wchan,comm|awk$2D输出PID S WCHAN COMMAND123D io_scheduledd456D wait_on_page_bit mysqld这个例子中进程dd(PID 123) 卡在了 io_schedule 即磁盘读写可能的原因是磁盘慢、坏道、NFS 挂了。而进程mysqld(PID 456)卡在了wait_on_page_bit即内存页回写可能的原因是内存压力大。也可以直接读取某进程的wchan例如cat/proc/798/wchan可能输出为rpc_wait_bit_killable即卡在RPC 层。然后可以通过stack中内容进一步排查具体卡在了哪个函数例如在刚才的例子中继续检查stack:cat/proc/789/stack可能输出为[0]rpc_wait_bit_killable0x??/0x??[sunrpc][0]__rpc_execute0x??/0x??[sunrpc][0]rpc_run_task0x??/0x??[sunrpc][0]nfs_write_rpc0x??/0x??[nfs][0]nfs_file_write0x??/0x??[nfs][0]vfs_write0x??/0x??[0]sys_write0x??/0x??由此能看出是 NFS 写操作卡在 RPC 层。2. hung_task当 D 状态进程超过 120 秒默认内核会主动打印带有hung task 关键字的log因此使用dmesg 指令可以可以获取dmesg-T|grephung_task可能输出为[Thu May2510:15:322026]INFO: task dd:123 blockedformorethan120seconds.[Thu May2510:15:322026]Tainted: G W[Thu May2510:15:322026]echo 0 /proc/sys/kernel/hung_task_timeout_secsdisables this message.[Thu May2510:15:322026]ddD12310x00000000[Thu May2510:15:322026]Call Trace:[Thu May2510:15:322026][ffffffffa0000000]io_schedule0x??/0x??可以看出 ddPID 123卡在 io_schedule 超过 120 秒。2. Z 状态排查方法wchan、hung_task 等指令对Z状态无用我们需要一直找到进程的父进程进行处理。例如我们已经找到了一个Z状态进程psaux|awk$8Z输出USERPID STAT COMMAND root123Z[sh]defunct我们需要找到它的父进程ps-oppid-p123例如输出456我们通知父进程回收kill-CHLD456以此来回收Z状态子进程123.如果不行可以选择直接杀死父进程kill-94563.防止Z状态产生最好防止Z状态产生的方式实在代码中确保父进程回收子进程的退出状态。例如一个非阻塞式回收的代码示例如下#includesys/wait.h#includesignal.h#includeunistd.h#includestdio.h#includeerrno.hvoidsigchld_handler(intsigno){intsaved_errnoerrno;while(waitpid(-1,NULL,WNOHANG)0);// 回收所有已结束的子进程errnosaved_errno;}intmain(){structsigactionsa;sa.sa_handlersigchld_handler;sigemptyset(sa.sa_mask);sa.sa_flagsSA_RESTART|SA_NOCLDSTOP;sigaction(SIGCHLD,sa,NULL);pid_t pidfork();if(pid0){printf(Child exiting\n);return0;}elseif(pid0){printf(Parent doing other work...\n);sleep(2);// 模拟父进程的工作}return0;}或者使用通过第二次 fork使实际工作的子进程成为孤儿被 initPID1接管。init 会自动回收子进程从而避免僵尸。#includesys/wait.h#includeunistd.h#includestdio.hintmain(){pid_t pidfork();if(pid0){// 第一次子进程pid_t pid2fork();if(pid20){// 第二次子进程实际工作的进程printf(Working child (will be adopted by init)\n);sleep(2);return0;}elseif(pid20){// 第一次子进程直接退出使第二次子进程成为孤儿return0;}}elseif(pid0){// 父进程回收第一次子进程很快完成wait(NULL);printf(Parent: first child reaped, grandchild adopted by init\n);sleep(3);}return0;}
Linux CPU性能优化:D状态和Z状态排查与处理
文章目录一、Linux进程五大基本状态1. 运行状态RRunning / Runnable2. 可中断睡眠状态SInterruptible Sleep3. 不可中断睡眠状态DUninterruptible Sleep4. 停止状态TStopped / Traced5. 僵死状态ZZombie / Defunct二、D 状态和Z状态排查1.D 状态排查方法1. wchan stack2. hung_task2. Z 状态排查方法3.防止Z状态产生一、Linux进程五大基本状态在 Linux 系统中常用的五大基本状态通过 ps、top 等命令可查看分别是1. 运行状态RRunning / Runnable含义进程当前正在运行占用 CPU或者处于可运行队列中、只要获得 CPU 时间片就能立刻执行。特点包括正在 CPU 上执行的进程以及就绪等待调度的进程。这是进程争取 CPU 时的活跃状态。示例一个不停计算的 while(1) 程序通常处于 R 状态在多核系统上多个 R 状态进程可能同时运行。2. 可中断睡眠状态SInterruptible Sleep含义进程正在等待某个事件完成如等待 I/O 输入、等待锁、等待信号等并且该睡眠可以被信号唤醒。特点最常见的睡眠状态大部分时间系统进程都在此状态。当等待的资源可用或收到信号时进程会回到 R 状态。可以被 kill 命令或其它信号打断。示例Shell 等待用户输入、网络服务等待客户端连接、进程调用 sleep() 或 nanosleep()。3. 不可中断睡眠状态DUninterruptible Sleep含义进程正在等待某些不可被中断的 I/O 操作如直接读写磁盘、等待硬件响应期间无法响应任何信号。特点通常用于磁盘 I/O、某些设备驱动中的关键操作。即使发送 SIGKILL 也无法立即终止该进程必须等待 I/O 完成或系统重启。这种设计是为了防止在关键数据写回磁盘时被中断导致数据不一致。示例sync 命令刷新缓冲区、dd 直接写入块设备、NFS 因网络故障僵住时的等待操作。注意短时间的 D 状态是正常的若长时间存在且数量增多往往提示 I/O 瓶颈或存储设备问题。4. 停止状态TStopped / Traced含义进程的执行被暂停通常是由于收到了 SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU 等信号或者被调试器如 gdb暂时接管。特点进程不会获得 CPU 时间也不响应普通信号除 SIGKILL、SIGCONT 外。可以通过发送 SIGCONT 信号让进程恢复到 R 状态继续执行。示例在终端按下 CtrlZ 将前台进程挂起 或 gdb 中设置断点后命中暂停 或 使用 kill -SIGSTOP 显式暂停。5. 僵死状态ZZombie / Defunct含义进程已经结束运行调用了 exit 或收到终止信号但其进程描述符task_struct仍然保留等待父进程调用 wait() 或 waitpid() 来读取其退出状态。特点该进程不再占用任何内存或 CPU 资源只保留内核中的一个极小结构体。无法被杀死因为它已经“死”了只能通过让父进程回收它来消除。若父进程不回收且不退出僵尸进程会一直存在若父进程先退出僵尸进程会被 initPID1进程收养并自动回收。示例父进程编写不当未调用 wait导致子进程结束后一直处于 Z 状态。大量僵尸进程可能耗尽进程号上限影响系统运行。本文将具体介绍D状态和Z状态的排查与处理。二、D 状态和Z状态排查1.D 状态排查方法1. wchan stackwchanwait channel能看到 D 状态进程“卡”在内核的哪个具体函数例如ps-eopid,state,wchan,comm|awk$2D输出PID S WCHAN COMMAND123D io_scheduledd456D wait_on_page_bit mysqld这个例子中进程dd(PID 123) 卡在了 io_schedule 即磁盘读写可能的原因是磁盘慢、坏道、NFS 挂了。而进程mysqld(PID 456)卡在了wait_on_page_bit即内存页回写可能的原因是内存压力大。也可以直接读取某进程的wchan例如cat/proc/798/wchan可能输出为rpc_wait_bit_killable即卡在RPC 层。然后可以通过stack中内容进一步排查具体卡在了哪个函数例如在刚才的例子中继续检查stack:cat/proc/789/stack可能输出为[0]rpc_wait_bit_killable0x??/0x??[sunrpc][0]__rpc_execute0x??/0x??[sunrpc][0]rpc_run_task0x??/0x??[sunrpc][0]nfs_write_rpc0x??/0x??[nfs][0]nfs_file_write0x??/0x??[nfs][0]vfs_write0x??/0x??[0]sys_write0x??/0x??由此能看出是 NFS 写操作卡在 RPC 层。2. hung_task当 D 状态进程超过 120 秒默认内核会主动打印带有hung task 关键字的log因此使用dmesg 指令可以可以获取dmesg-T|grephung_task可能输出为[Thu May2510:15:322026]INFO: task dd:123 blockedformorethan120seconds.[Thu May2510:15:322026]Tainted: G W[Thu May2510:15:322026]echo 0 /proc/sys/kernel/hung_task_timeout_secsdisables this message.[Thu May2510:15:322026]ddD12310x00000000[Thu May2510:15:322026]Call Trace:[Thu May2510:15:322026][ffffffffa0000000]io_schedule0x??/0x??可以看出 ddPID 123卡在 io_schedule 超过 120 秒。2. Z 状态排查方法wchan、hung_task 等指令对Z状态无用我们需要一直找到进程的父进程进行处理。例如我们已经找到了一个Z状态进程psaux|awk$8Z输出USERPID STAT COMMAND root123Z[sh]defunct我们需要找到它的父进程ps-oppid-p123例如输出456我们通知父进程回收kill-CHLD456以此来回收Z状态子进程123.如果不行可以选择直接杀死父进程kill-94563.防止Z状态产生最好防止Z状态产生的方式实在代码中确保父进程回收子进程的退出状态。例如一个非阻塞式回收的代码示例如下#includesys/wait.h#includesignal.h#includeunistd.h#includestdio.h#includeerrno.hvoidsigchld_handler(intsigno){intsaved_errnoerrno;while(waitpid(-1,NULL,WNOHANG)0);// 回收所有已结束的子进程errnosaved_errno;}intmain(){structsigactionsa;sa.sa_handlersigchld_handler;sigemptyset(sa.sa_mask);sa.sa_flagsSA_RESTART|SA_NOCLDSTOP;sigaction(SIGCHLD,sa,NULL);pid_t pidfork();if(pid0){printf(Child exiting\n);return0;}elseif(pid0){printf(Parent doing other work...\n);sleep(2);// 模拟父进程的工作}return0;}或者使用通过第二次 fork使实际工作的子进程成为孤儿被 initPID1接管。init 会自动回收子进程从而避免僵尸。#includesys/wait.h#includeunistd.h#includestdio.hintmain(){pid_t pidfork();if(pid0){// 第一次子进程pid_t pid2fork();if(pid20){// 第二次子进程实际工作的进程printf(Working child (will be adopted by init)\n);sleep(2);return0;}elseif(pid20){// 第一次子进程直接退出使第二次子进程成为孤儿return0;}}elseif(pid0){// 父进程回收第一次子进程很快完成wait(NULL);printf(Parent: first child reaped, grandchild adopted by init\n);sleep(3);}return0;}