【Linux操作系统】系统进程状态与类型详解

一、进程状态概念

Linux进程状态是指Linux系统中进程所处的不同执行阶段或条件,状态决定了一个进程的后续动作

在Linux中,状态本质上是进程taskt_struct结构体中的一个整形变量
在这里插入图片描述

体现在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 */
};

二、Linux中的进程状态:

就绪状态(Ready):进程已经准备好执行但是还没有被处理器调度执行的状态

运行状态(Running):进程正在被处理器执行的状态

阻塞状态(Blocked):进程因为等待某些事件(如等待I/O操作完成、等待信号量等)而被阻塞的状态

等待状态(Waiting):进程因为等待某些事件(如I/O操作完成、信号等)而被阻塞的状态

暂停状态(Paused):进程被暂停执行,可以自行恢复执行

挂起状态(Suspended):进程被操作系统挂起执行,资源被释放,进程状态被保存到磁盘上。挂起状态可以分为内存挂起状态和磁盘挂起状态两种。

终止状态(Terminated):进程执行完毕或者因为某些原因被终止的状态

1.就绪状态、运行状态(R)

一个进程已经准备就绪,可以随时被系统调度此时就是运行状态
相当于合并了就绪和运行,统称运行状态

注意:不要认为被调度执行后才算运行状态

1.运行队列(进程排队)

我们知道,在等待运行时,进程需要排队等待被调度,就有一个就绪队列

但当内存中存在许多进程时就会使得CPU中的资源不够同时分配给许多进程,于是从就绪队列调度后仍然存在一个队列–运行队列

需要特别注意的是,在Linux中进程的任何排队操作,都是进程的PCB在排队,把所有的进程PCB用数据结构组织起来,就形成了我们的运行队列

这些存在运行队列中的进程都被称作运行状态

在这里插入图片描述
在这里插入图片描述

  • 不过一个进程的PCB可能不仅仅在一个队列中排队,在每个task_struct结构体中都会有很多的structural_listnode n,真正去排队的是这些东西,而不是之前的简化说法PCB

在这里插入图片描述

2.时间片

一个进程被拿到cup中执行时,并不是等这个进程执行完毕才切换下一个进程
而是这多个进程在一个时间段内所有代码都会被执行 —— 这就叫做【并发执行】
每一个进程执行一个时间片的时间后就会从cpu上拿下来,然后放上下一个进程。这个动作就称作为【进程切换】。
一个时间片通常是10ms左右

3.进程的前后台运行

1.前台进程

  • 指在终端中直接运行的命令或程序,它会占据当前终端,并将执行过程中产生的相关信息显示在终端上
  • 前台进程与用户进行交互,需要较高的响应速度,因此其优先级通常较高
  • 如果前台进程没有结束,用户则不能在当前终端中进行其他的操作

在Shell命令行中输入并执行某条命令,默认情况下会启动一个前台进程

2.后台进程

  • 也称为守护进程(Daemon),是运行在后台的一种特殊进程
  • 后台进程基本上不和用户交互,其优先级别相对较低
  • 后台进程不受终端控制,即使终端关闭,它也不会随之消失,而是继续运行
    在这里插入图片描述

在这里插入图片描述

比如我写了一个死循环,上面的状态显示的是R+

  • 这里的R+代表的就是这个进程是在前台运行的,所以我们在输入任何指令后不会对其造成任何的影响
  • 如果想要运行在后台,需要在执行时在你生成的可执行文件的后面加上了一个&,那么其状态变成了R,此代表的意思就是这个进程它是运行在了后台
    在这里插入图片描述
    在这里插入图片描述

2.挂起状态

挂起状态是一个重要的概念,它指的是将当前处于运行状态的数据保存在内存中,并让进程或系统等待某个事件的到来再继续执行,这相当于使计算机或进程进入一种睡眠状态

挂起状态分为系统挂起和进程挂起

1.系统挂起

  • 整个计算机系统进入挂起状态,此时CPU停止工作,内存中的数据被保存到磁盘中,以便在系统唤醒时能够快速恢复。
  • 系统挂起通常通过特定的命令或系统设置来实现,如使用rtcwake命令设置系统挂起时间。

2.进程挂起

  • 单个进程被挂起,暂停其所有活动并释放CPU时间片,但进程的状态信息和资源(如内存中的数据)仍然被保留。
  • 进程挂起可以由用户主动发起,也可能是操作系统因为资源不足或其他原因而自动进行。
  • 进程挂起可以通过发送特定的信号(如SIGSTOP)来实现,也可以通过其他机制(如调试器的暂停功能)来触发

3.挂起的场景与特点

  • 在挂起状态下,系统或进程占用的资源(如CPU、内存等)非常低,有助于释放系统资源给其他任务使用
  • 在资源有限的情况下,可以通过挂起非关键进程来释放资源给更重要的任务使用。

一般计算机处于挂起状态的前提是计算机的资源已经比较吃紧了,不得不将一些数据换到磁盘中(磁盘中的swap分区)

在这里插入图片描述

3.阻塞状态(S、D)

阻塞状态是进程在等待某个事件或资源(如I/O操作完成、信号量释放等)时所处的状态。

在这种状态下,进程无法继续执行,因为它需要等待的资源或事件尚未发生。

此时进程不会占用CPU资源,操作系统会调度其他可执行的进程

例如我们平时的命令行等待我们输入命令时就是一个阻塞状态

1.可中断的睡眠状态(S浅度睡眠状态)

S(sleeping)在本质上也是阻塞状态的一种,可以说是阻塞的一种分支

S状态是Linux中可中断的睡眠状态,表示进程正在等待某个事件或资源

S状态下的进程可以被信号或中断唤醒并继续执行。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

将进程运行起来我们可以看到其是S+的状态,因为命令行此时正在等待用户的输入,遇到I/O操作。

2.不可中断的睡眠状态(D深度睡眠状态)

D状态是Linux中不可中断的睡眠状态,也称为磁盘睡眠状态。在这种状态下,进程正在等待磁盘I/O操作或其他无法被中断的硬件操作完成。

进程无法响应任何信号或中断。

进程只能等待操作完成后才能继续执行。

3.关于S浅度睡眠状态与D深度睡眠状态的理解

学了挂起状态,我们可以知道,在内存不足时,会通过挂起状态来释放我们的内存

但是如果在极端情况下,即使挂起状态释放内存也不够呢?这时操作系统就没办法了,只能亲自动手开始杀进程了

但是这时用户有一个特别重要的信息要通过进程写入磁盘,但这个进程又刚好被操作系统杀了呢?这可就麻烦了,信息丢失可是个大问题

于是为了保证某些正在干重要事情的进程不被杀掉,给这些进程加了一块“免死金牌” D状态,操作系统在杀进程时,如果进程只是一个普通的‘S’,操作系统可以杀,但如果是‘D’,那么因为有免死金牌,操作系统不能杀

这个免死金牌具体来说就是给该进程的状态设置为深度睡眠状态。那么这个进程就不会被杀死了,当磁盘写完数据后告知进程,那么它就可以将自己放入【运行队列】里去运行,此时它的状态就变成R了

那如果D状态的进程特别多呢?

一般系统中D状态的进程是很少见的,如果你看到了D状态的进程,那就说明你的系统里挂不远了,洗洗睡吧

4.停止状态(T)、进程跟踪状态(t)

1.停止状态(T)

  • 停止状态(T)表示进程已经被停止执行。这通常是因为进程接收到了某种信号(如SIGSTOP)而主动或被动地停止了运行
  • 在停止状态下,进程不会占用CPU资源,也不会执行任何操作,直到它接收到继续执行的信号(如SIGCONT)或系统对其进行其他操作。

这种状态通常发生在以下情况下:

当进程读取一个设备,但是被设备拒绝了,而操作系统又不想杀掉你,此时,为了减少你的非法操作,就会把该进程置为停止状态。
当用户使用Ctrl+Z组合键将一个正在前台运行的进程移至后台时,该进程会进入停止状态

  • 在停止状态下,进程仍然存在于系统中,但不再活动。可以使用fg命令将一个停止的进程移回前台继续执行,或者使用bg命令将其放入后台执行。
  • 系统管理员可以使用kill命令的-STOP选项,或者kill -s SIGSTOP命令,将一个进程暂停
  • 暂停进程:kill -19 PID
  • 启动进程:kill -18 PID

处于暂停状态的进程,不仅仅是等待硬件资源,也可能是等待软件信号等,所以,他也可以被看作是一个阻塞状态的分支

2.进程跟踪状态(t)

  • 进程跟踪状态(t)是进程在被调试器跟踪时所处的状态。当使用调试器(如gdb)对进程进行调试时,如果调试器暂停了进程的执行(例如,在断点处),则进程会进入跟踪状态。
  • 在跟踪状态下,进程同样不会执行任何操作,但它与停止状态(T)的区别在于,跟踪状态是由调试器主动控制的,而停止状态则可能是由信号或其他外部因素触发的。
  • 比如调试器暂停了进程的执行。这通常发生在调试器设置了断点,并且进程执行到该断点时,或者调试器执行了其他导致进程暂停的操作,如单步执行等

T停止状态和S睡眠状态的主要区别在于:

  • T状态是进程被显式停止执行的状态,需要通过发送信号来恢复;
  • S状态是进程在等待某个事件或资源完成的状态,当等待条件满足时进程会自动恢复执行

5.死亡状态(X)

死亡状态就是所有资源释放进程的任务结束了,使这个进程退出,同时把这个进程的资源释放(通过父进程回收)
(就如一个人过世,处理好他的后事)

杀进程的方法:
1.发送9信号

kill -9 PID

2.通过进程名、

killall 进程名

在这里插入图片描述

6.僵死状态(Z)

1.僵死状态的概念

僵死状态形象点说有点像是没死透的死亡状态

进程在死亡后需要将进程的资源给父进程回收

简单说就是父进程既然创建了子进程后,定然会给子进程布置了任务,子进程完成任务后需要将任务的完成情况报备给父进程,否则父进程怎么知道你有没有好好完成任务呢?

因此,进程在退出后,却没有把进程的资源给父进程回收,此时的进程就处于僵死状态,此时的进程又被称作僵尸进程

2.进程PID与退出码

在系统中每个进程在创建时都会给进程分配一个PID,子进程的PID是父进程用来识别和操作子进程的唯一标识符

在退出时会返回给父进程的一个值,用于表示子进程的退出状态或执行结果。父进程通过读取子进程的退出码来获取子进程的结束信息
在这里插入图片描述
父进程回收子进程时会用wait函数回收子进程的PID,并将子进程的退出状态保存在通过第二个参数传递的地址中(如果提供了该参数)

3.僵尸进程举例

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>

int main()
{
   pid_t pid=fork();//创建父子进程

   if(pid==0) //子进程进
   {
        //.......
        printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid());
        exit(0);//返回退出码
        //子进程结束
   }
    //父进程
    sleep(15);
    int status;
    pid_t p=wait(&status);
    printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());

}

在sleep(15)前,子进程结束,但是还没有被父进程回收,显示的是Z+僵死状态
在这里插入图片描述
休眠15秒过后,父进程对子进程进行回收,子进程的僵死状态结束
在这里插入图片描述

4.僵尸进程的危害

如果一个父进程一直不去不去回收已经退出的子进程的信息,就会造成内存泄露

进程一般退出的时候,它的代码和相关数据会被释放,但是,它的task_struct对象依然还在内存中,仍然占用内存,占用进程表,这些僵尸进程会累积起来,占用大量的进程表项。
进程表是有限资源,当进程表被填满后,系统可能无法再创建新的进程,导致系统资源泄露和性能下降。

5.僵尸进程的解决方案

  1. 首先自然就是在子进程结束后让父进程用wait、waitpid回收
  2. 让父进程忽略 SIGCHLD信号,这样子进程结束后会被自动回收,不会变成僵尸进程
  3. 杀死父进程,让子进程成为孤儿进程,由 init 进程接管并回收

7.孤儿进程

1.概念

前面我们知道,僵尸进程是能够恢复的,只要被父进程回收就行

可万一,子进程好好地,父进程出事了呢?

父进程如果提前退出,子进程后退出,变成僵尸进程之后,那该如何处理呢?这种情况下的进程就称作孤儿进程

  • 孤儿进程不再拥有原来的父进程,因为父进程已经终止

2.特点

但其实这种情况比僵尸进程要好的多,因为在Linux系统中,存在init进程

  • 孤儿进程会被init进程收养。这意味着这些进程将以init进程作为它们的父进程继续运行
  • 当孤儿进程结束时,其占用的资源(如内存、文件描述符等)会被正常回收,就像任何其他进程一样
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
	pid_t id = fork();
	if (id < 0) {
		perror("fork");
		return 1;
	}
	else if (id == 0) {//child
		printf("I am child, pid : %d\n", getpid());
		sleep(10);
	}
	else {//parent
		printf("I am parent, pid: %d\n", getpid());
		sleep(3);
		exit(0);
	}
	return 0;
}

3.孤儿进程解决僵尸进程

甚至于如果出现了僵尸进程,我们还可以将僵尸进程的父进程杀掉,这样僵尸进程就会变成孤儿进程,这样就能正常被init进程回收

孤儿进程的存在通常不是一个问题,因为系统能够自动处理它们。然而,在某些情况下,如果系统中产生了大量的孤儿进程,并且这些进程长时间运行而不结束,那么它们可能会占用系统资源,影响系统性能

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饿了我会自己捡代码吃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值