linux_进程状态

目录

[一. 概念铺设](#一. 概念铺设)

状态是什么?

传统操作系统的状态转换图

[二. 传统操作系统状态](#二. 传统操作系统状态)

[1. 运行](#1. 运行)

[2. 阻塞](#2. 阻塞)

[3. 挂起](#3. 挂起)

[三. linux 中的进程状态](#三. linux 中的进程状态)

[1. 总体介绍](#1. 总体介绍)

[2. R](#2. R)

[3. S](#3. S)

[4. D](#4. D)

[kill -9](#kill -9)

[D vs S](#D vs S)

[5. T](#5. T)

kill

[T vs S](#T vs S)

[6. Z](#6. Z)

什么是僵尸状态?

僵尸进程的危害

[7. D](#7. D)

父进程的作用

[8. 孤儿进程](#8. 孤儿进程)


一. 概念铺设

状态是什么?

状态就是一个用来描述该进程当前正在做什么。

传统的操作系统的状态有:

  • 运行
  • 阻塞
  • 挂起

传统操作系统的状态转换图

二. 传统操作系统状态

1. 运行

我们知道,在我们现在的操作系统中有许多进程(我们现在的系统多数都是时间片轮转调用的),但是一般的电脑只有一个 CPU ,而进程要运行必须要使用 CPU ,所以在同一时刻肯定是只有一个进程占用CPU资源,但是在用户的观感上好像我们计算机上的进程都是同时在运行的。

实际上,在操作系统中有一个队列叫做运行队列,而在运行队列中的进程就是运行态,并不是说只有当进程在运行的那一刻才是运行状态

所以运行状态就是在运行队列里面等待的进程就是运行状态,对于时间片轮状的操作系统来说,每一个进程每一次只执行特定长的时间,如果执行没有结束就继续到等待队列的后面继续排队,如果执行完毕,那么就从等待队列中删掉。

2. 阻塞

阻塞也是传统的操作系统书中的一个状态,而阻塞就是当一个进程需要某种外设资源的时候的状态。

比如说,当我们使用C语言进行 scanf 的时候,或者使用 C++ cin 的时候,此时就是阻塞状态,就是该进程需要等待键盘资源输入,或者也有就是向显示器上打印等...

3. 挂起

挂起状态时常不会用到的,挂起状态就是当操作系统内资源不足的时候,当一个进程不运行,但是占着操作系统资源,然后此时操作系统还时资源紧张,那么操作系统会将该进程的代码和数据换到磁盘里面,当该进程运行的时候才会将该进程的代码和数据继续换入到内存中,所以挂起就是一个进程的PCB在内存中,但时它的代码和数据被换到磁盘里面就是挂起状态。

三. linux 中的进程状态

1. 总体介绍

  • 运行 (R):进程正在运行或已经准备好运行。
  • 休眠 (S):进程处于休眠状态,等待某个事件的发生,如I/O操作或信号。
  • 中断 (D):进程处于不可中断的睡眠状态,通常是等待设备或资源,如磁盘I/O。
  • 僵尸 (Z):进程已经终止,但是其父进程还未收到终止信号或未进行处理,进程存在但没有参与运行。
  • 停止 (T):进程被暂停或停止,通常是由于收到停止信号,如Ctrl+Z。
  • 僵尸 (X):进程已经终止,但是其父进程已经终止或退出,僵尸进程被init进程(PID为1)接管。

2. R

在 linux 中的 R 状态就是对应的是运行状态。

那么下面就写一个代码来查看该进程的状态。

cpp 复制代码
int main()
{
  while(1)
  {
    printf("hello world\n");
    sleep(1);
  }
  return 0;
}

我们还是使用 ps 来查看进程的状态。

bash 复制代码
[lxy@hecs-165234 linux3]$ ps axj | head -1 && ps axj | grep proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  8145  8145  6468 pts/1     8145 S+    1000   0:00 ./proc

这里看到,我们查到该进程的状态是 S,为什么呢?因为我们之前说过,我们的进程一般在runQueue里面才算是执行,那么当我们打印的时候我们需要等待外设(显示器),然后由于外设比较慢,所以大多数时间都是再等待,所以我们就看到的是S状态,并不是R。

所以这时候我们可以去掉打印,我们一直死循环,我们就可以看到 R 状态了。

cpp 复制代码
int main()
{
  while(1);
  return 0;
}
bash 复制代码
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  8453  8453  6468 pts/1     8453 R+    1000   0:02 ./proc

这时候查看到的就是 R 状态,这里为什么是R+ 呢? 这里的+表示在前台运行,也就是在 bash 前端运行。

3. S

s状态其实我们刚才已经看到了,就是打印的时候我们一直看到的是S状态,这是因为我们在等待显示器资源。

4. D

D状态其实也就是睡眠状态,那么和S状态有什么不同呢?

其中S是浅度睡眠,而D是深度睡眠。

浅度睡眠是可以被杀死的,而深度睡眠不能被杀死。

这里穿插一条知识。

kill -9

kill 是信号,kill -9 是一条杀死进程的信号,后面加进程PID 就可以向进程发送该信号,而D状态就是不能被杀死。

D vs S

D状态和S状态的区别?

场景:

当一个进程向磁盘写入很大的数据的时候,该进程在等待,那么这个时候如果操作系统直接杀死该进程,那么向磁盘中写入数据后失败或者成功,磁盘都会向该进程反馈,但是此时操作系统杀死了该进程,如果写入失败,导致磁盘数据被丢失,那么后果还是很严重的,所以在进程向磁盘写入数据的时候,进程等待磁盘返回的时候,此时的进程就是处于D状态也就是胜读睡眠状态,这时候的进程不能被杀死。

由于这个状态不好演示,所以这里也就不演示了。

5. T

T状态就是停止状态,就是该进程停止了,我们也可以通过信号来控制。

bash 复制代码
[lxy@hecs-165234 linux3]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

[lxy@hecs-165234 linux3]$ ps axj | grep proc
 6468  8501  8501  6468 pts/1     8501 R+    1000   0:26 ./proc
 6180  8503  8502  6180 pts/0     8502 R+    1000   0:00 grep --color=auto proc
[lxy@hecs-165234 linux3]$ kill -19 8501

kill

kill -l 是查看信号的命令,19 号信号是一个告诉进程停止的信号,所以我们向我们的进程发送19号信号,然后我们在继续查看该进程的状态。

cpp 复制代码
[lxy@hecs-165234 linux3]$ ps axj | grep proc
 6468  8501  8501  6468 pts/1     8504 T     1000   0:54 ./proc

此时就是T状态,那么我们怎么恢复呢? 我们使用18号信号,这里也就不演示了,但是这里说明一下,如果从18号信号恢复过来的话,就变成后台运行了,所以我们 ctrl + c就干不掉了,我们需要使用 kill -9 来杀死它。

T vs S

T和S状态的区别,我们这里感觉T 和 S没有什么区别,但是还是有区别的,S状态就是进程在等待外设资源的一种状态,而T是可能在等待资源,但是也不一定是在等待资源。

6. Z

Z 是僵尸状态。

什么是僵尸状态?

场景:国外发生的一起凶杀案,现在有人看到并且报警,此时警察过来首先先检验受害人是否死亡,或者死亡原因,当检验完毕后,并且警方处理完了所有的事情然后等待受害者家属来为受害者进行后续操作,在受害者家属来之前,受害者就是属于僵尸状态。

所以当一个进程死亡后,如果关心它的进程(也就是父进程)没有来接收子进程的返回的数据等,那么此时的子进程就是僵尸状态。

我们下面让子进程运行5秒后退出,然后父进程持续运行,我们来观察此时的状态。

cpp 复制代码
int main()
{
  pid_t id = fork();

  if(id == 0)
  {
    int count =  5;
    while(count--)
    {
      printf("I am a child... PID: %d, PPID: %d, time: %d\n", getpid(), getppid(), count);
      sleep(1);
    }
  }
  else 
  {
    while(1)
    {
      printf("I am a father... PID: %d, PPID: %d\n", getpid(), getppid());
      sleep(1);
    }
  return 0;;
}

我们此时在写一个监控脚本来看这里的变化。

bash 复制代码
[lxy@hecs-165234 linux3]$ while : ; do ps axj | head -1 && ps axj | grep proc | grep -v grep; sleep 1; done

这时候我们这个脚本就会查看 proc 的PCB

bash 复制代码
[lxy@hecs-165234 linux4]$ ./proc 
I am a father... PID: 9542, PPID: 6468
I am a child... PID: 9543, PPID: 9542, time: 4
I am a father... PID: 9542, PPID: 6468
I am a child... PID: 9543, PPID: 9542, time: 3
I am a father... PID: 9542, PPID: 6468
I am a child... PID: 9543, PPID: 9542, time: 2
I am a father... PID: 9542, PPID: 6468
I am a child... PID: 9543, PPID: 9542, time: 1
I am a father... PID: 9542, PPID: 6468
I am a child... PID: 9543, PPID: 9542, time: 0
I am a father... PID: 9542, PPID: 6468
I am a father... PID: 9542, PPID: 6468
bash 复制代码
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9542  9542  6468 pts/1     9542 S+    1000   0:00 ./proc
 9542  9543  9542  6468 pts/1     9542 Z+    1000   0:00 [proc] <defunct>

这里看到,此时我们最后子进程退出了,然后父进程还在运行,这是子进程就是Z僵尸状态。

僵尸进程的危害

那么进程一直僵尸会怎么样呢?

  • 浪费系统资源:每个进程都需要一定的系统资源,如内存和描述符。当大量的僵尸进程存在时,会占用过多的系统资源,影响系统的正常运行和其他进程的执行效率。
  • 产生内核问题:僵尸进程过多可能会导致系统的内核出现问题。尽管现代操作系统可以处理一定数量的僵尸进程,但如果没有及时清理,可能会导致内核资源不足或崩溃。
  • 父进程遭受拖累:僵尸进程的父进程可能会受到影响。如果父进程没有正确处理僵尸进程,它们的退出状态将一直保存在内核中,导致父进程的进程表中存在大量的僵尸进程。

虽然僵尸进程本身不会对系统造成直接的危害,但当过多的僵尸进程堆积时,会消耗系统资源并可能导致系统不稳定。因此,及时清理僵尸进程非常重要。一般来说,操作系统的init进程会负责清理僵尸进程,当子进程退出后,init进程会接管并回收僵尸进程的资源。

7. D

D状态就是死亡 dead 状态,也就是退出状态,而在每一个D状态之前都会有一段时间是Z状态的,因为这里的父进程要回收子进程。

父进程的作用

所以这里就体现了为什么要有父进程,因为父进程要对退出的子进程进行回收。

8. 孤儿进程

孤儿进程,见名知意,就是没有父进程的进程,我们下面演示一下。

我们写一个代码,让子进程一直运行,让父进程提前退出,然后我们打开监控脚本看一下。

cpp 复制代码
int main()
{
  pid_t id = fork();

  if(id == 0)
  {
    while(1)
    {
      printf("I am a child... PID: %d, PPID: %d\n", getpid(), getppid());
      sleep(1);
    }
  }
  else 
  {
    int count = 5;
    while(count--)
    {
      printf("I am a father... PID: %d, PPID: %d\n", getpid(), getppid());
      sleep(1);
    }
  }
  return 0;;
}

运行,打开监控脚本查看。

bash 复制代码
[lxy@hecs-165234 linux4]$ ./proc 
I am a father... PID: 9780, PPID: 6468
I am a child... PID: 9781, PPID: 9780
I am a father... PID: 9780, PPID: 6468
I am a child... PID: 9781, PPID: 9780
I am a father... PID: 9780, PPID: 6468
I am a child... PID: 9781, PPID: 9780
I am a father... PID: 9780, PPID: 6468
I am a child... PID: 9781, PPID: 9780
I am a father... PID: 9780, PPID: 6468
I am a child... PID: 9781, PPID: 9780
I am a child... PID: 9781, PPID: 1
bash 复制代码
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9780  9780  6468 pts/1     9780 S+    1000   0:00 ./proc
 9780  9781  9780  6468 pts/1     9780 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 6468  9780  9780  6468 pts/1     9780 S+    1000   0:00 ./proc
 9780  9781  9780  6468 pts/1     9780 S+    1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1  9781  9780  6468 pts/1     6468 S     1000   0:00 ./proc
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1  9781  9780  6468 pts/1     6468 S     1000   0:00 ./proc

这里还是只展示部分。

我们看到我们的父进程退出了,然后子进程的PPID变成了1,那么1是什么呢?

bash 复制代码
[lxy@hecs-165234 linux3]$ ps axj | head -1 && ps axj | grep 1
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     1     1     1 ?           -1 Ss       0  26:38 /usr/lib/systemd/systemd --system --deserialize 23

这里看到我们的1就是system 所以我们的子进程就被系统领养,此时我们的子进程的退出就由操作系统回收了。

相关推荐
吃肉不能购1 小时前
Label-studio-ml-backend 和YOLOV8 YOLO11自动化标注,目标检测,实例分割,图像分类,关键点估计,视频跟踪
运维·yolo·自动化
学Linux的语莫1 小时前
Ansible使用简介和基础使用
linux·运维·服务器·nginx·云计算·ansible
qq_312920111 小时前
docker 部署 kvm 图形化管理工具 WebVirtMgr
运维·docker·容器
踏雪Vernon1 小时前
[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式
linux·docker·容器·harmonyos
Onlooker1291 小时前
云服务器部署WebSocket项目
服务器
学Linux的语莫2 小时前
搭建服务器VPN,Linux客户端连接WireGuard,Windows客户端连接WireGuard
linux·运维·服务器
legend_jz2 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py2 小时前
【Linux】-学习笔记04
linux·笔记·学习
黑牛先生2 小时前
【Linux】进程-PCB
linux·运维·服务器
Karoku0662 小时前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch