Linux进程状态是进程在其生命周期中所处的不同状况,主要包括运行(R)、睡眠(S)、磁盘休眠(D)、停止(T)、僵尸(Z)和死亡(X)。这些状态反映了进程是正在执行、等待资源还是已经终止。其中,僵尸状态是一个关键概念,指子进程已退出但其退出信息未被父进程回收,导致其进程控制块无法释放,从而占用系统资源。若父进程先退出,子进程则会成为“孤儿进程”,并由1号init进程接管,以确保其最终能被正确回收,避免资源泄漏。
一个进程从创建而产生至撤销而消亡的整个生命期间,有时占有处理器执行,有时虽然可以运行但分不到处理器,有时虽然有空闲处理器但因等待某个时间的发生而无法执行,这一切都说明进程和程序不相同,进程是活动的且有状态变化的,于是就有了进程状态这一概念。

//linux源代码如下
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*/
};注意:
ps ajx

ps aux

运行状态:一个进程处于运行状态,并不说明他一定正在运行中。 它可能正在CPU中运行;也可能在CPU对应的运行队列中准备接受调度。
//通过一个死循环来查看运行状态
1 #include<stdio.h>
2 int main()
3 {
4 while(1)
5 {
6
7 }
8
9 return 0;
10 } 
此时我们可以看到STAT(state)状态栏那一栏我们的进程对应的状态为R+,R是运行状态,+代表是前台运行,没有+就是后台运行。 我们的死循环程序会不断的重复进行代码的执行,占用CPU的资源,它也不满足访问外事进入到阻塞状态或者其他状态的情况,所以它是运行状态。
睡眠状态说明进程在等待某件事情的完成。 处于浅度睡眠状态的进程随时可以被唤醒,也可以被杀掉(这里的睡眠有时候也可叫做可中断睡眠(interruptible sleep))。
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1) {
printf("i am a process\n");
sleep(1); // 睡眠1秒
}
return 0;
}我们通过ps ajx | head -1 && ps ajx | grep 文件名来查看它的状态

sleep(1) 时,会发生一个关键的状态转变:进程会主动让出 CPU 的使用权,并进入等待队列。此时操作系统内核会将这个进程标记为睡眠状态(S 状态),因为进程明确表示它将在接下来的1秒钟内不需要 CPU 资源。这种睡眠不是被动的等待,而是进程通过系统调用主动发起的请求。printf 函数的执行确实需要极短的 CPU 时间,但相对于整整1秒的睡眠期来说,这个执行时间几乎可以忽略不计。当 printf 完成输出后,进程立即进入睡眠状态,在接下来的1秒内都处于这种等待状态。操作系统的进程调度器在扫描进程状态时,几乎总是捕捉到进程在睡眠而不是在运行。sleep() 函数在底层是通过设置定时器并调用等待函数来实现的。进程会挂起自己,直到定时器超时才会被重新唤醒。#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
return 0;
}我们的程序需要从键盘上读取数据,不断等待外设键盘准备好,如果我们不去敲击键盘输入数据,那么键盘就会一直不准备好,那么此时进程就会在键盘设备的阻塞队列中等待,此时就对应操作系统学科的阻塞状态,所以此时我们的进程处于休眠状态

而处于浅度睡眠状态的进程是可以被杀掉的,我们可以使用kill命令将该进程杀掉。

D磁盘休眠状态(disk sleep): 也叫做深度睡眠,处于这个状态的进程通常会等待IO的结束,不响应任何请求,同时其也对应操作系统学科上的阻塞状态的一种特殊情况,处于深度睡眠的进程,不响应操作系统的任何请求,也无法被 kill -9杀死 您描述的场景很好地解释了深度睡眠(Uninterruptible Sleep)的设计原理和必要性。在这个假设的磁盘写入困境中,确实展现了一个典型的数据完整性保护机制。
当进程向磁盘发起写入请求后,便进入等待回应的状态。
在Linux当中,我们可以通过发送SIGSTOP信号使进程进入暂停状态(stopped),发送SIGCONT信号可以让处于暂停状态的进程继续运行。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("i am proc,my PID:%d,my PPID:%d\n",getpid(),getppid());
sleep(6);
}
return 0;
}暂停:kill -SIGSTOP PID或者kill -19 PID

继续:kill -SIGCONT PID或者kill -18 PID

注意:这个状态只是一个返回状态,并不会在任务列表中看到这个状态,当Z僵尸状态结束后就会进入Z死亡状态
僵尸进程(zombies):子进程退出的时候,如果父进程没有主动读取回收子进程的信息,那么子进程会让自己一直处于Z僵尸状态,即对应子进程相关资源尤其是task_struct结构体不能释放。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("I am running...\n");
pid_t id = fork();
if(id == 0){ //child
int count = 5;
while(count){
printf("I am child...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);
sleep(1);
count--;
}
printf("child quit...\n");
exit(1);
}
else if(id > 0){ //father
while(1){
printf("I am father...PID:%d, PPID:%d\n", getpid(), getppid());
sleep(1);
}
}
else{ //fork error
}
return 0;
} 
程序开始运行时,初始进程处于睡眠状态,这是因为它等待外设屏幕输出主动让出CPU。当fork系统调用执行后,创建出的子进程与父进程各自进入独立的执行流,两者都因包含sleep调用而周期性处于睡眠状态。
子进程在完成五次输出后通过exit正常退出,此时进程的执行已经结束,但由于父进程正忙于自己的循环输出而没有调用wait系列函数来回收子进程的退出状态,子进程便进入了僵尸状态。僵尸状态的特点是进程的执行已终止,核心资源已被释放,但在进程表中仍保留着退出状态信息等待父进程读取,这时使用ps命令便能观察到标记为Z的子进程。
在Linux当中的进程关系大多数是父子关系,若子进程先退出而父进程没有对子进程的退出信息进行读取,那么我们称该进程为僵尸进程。 但若是父进程先退出,那么将来子进程进入僵尸状态时就没有父进程对其进行处理,此时该子进程就称之为孤儿进程。
若是一直不处理孤儿进程的退出信息,那么孤儿进程就会一直占用资源,此时就会造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程会被1号init进程领养,此后当孤儿进程进入僵尸状态时就由int进程进行处理回收。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("I am running...\n");
pid_t id = fork();
if(id == 0){ //child
int count = 5;
while(1){
printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid(), count);
sleep(1);
}
}
else if(id > 0){ //father
int count = 5;
while(count){
printf("I am father...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);
sleep(1);
count--;
}
printf("father quit...\n");
exit(0);
}
else{ //fork error
}
return 0;
} 

这里的bash进程算的上是当前子进程的爷爷进程,可是任何一个父进程只对子进程负责,即bash使用fork创建出来的当前子进程的父进程, bash只对当前子进程的父进程负责,bash的代码逻辑中没有要为当前子进程负责的逻辑,所以bash不能回收当前的子进程。
总的来说,Linux进程状态机制是操作系统进行资源管理和调度的核心。运行、睡眠、停止等状态确保了CPU等资源的合理分配;而僵尸和孤儿进程则揭示了进程间父子关系与资源回收的重要性。深刻理解这些状态及其转换,对于诊断进程问题、编写健壮的程序(尤其是正确回收子进程资源)以及维护系统稳定性和性能至关重要,是系统编程和运维的基础知识。