当前位置: 首页 > java >正文

Linux系统----进程的状态

在开启本章知识之前,我们要先了解一些前沿知识

1.进程相关链表结构

  在操作系统中,进程是资源分配和调度的基本单位。为了管理系统中的多个进程,操作系统内部分配了一系列的数据结构来描述进程,其中表链是重要的组织形式之一。每个进程可以用一个进程控制块(PCB)来描述,PCB 包含进程的多种信息,如进程的状态(就绪、运行、阻塞等)、进程的优先级、进程所拥有的资源等。

  在进程链表中,每个节点代表一个进程的 PCB。链表的链接是通过在 PCB 结构体中设置一个指向下一个进程 PCB 的指针(或者在双链表中还有指向前一个进程 PCB 的指针)来实现的。例如,在单链表情况下,PCB 结构体中可能有一个成员变量 next,其类型是指向本结构体的指针。当创建一个新的进程时,新的 PCB 节点会被分配内存,然后通过设置 next 指针,将其链接到已有的链表中。例如,假设有一个链表头指针 head 指向已存在的进程链表的第一个节点,当添加一个新节点 new_node 时,new_node->next = head,然后 head = new_node,这样就把新节点添加到链表头部。

2.链表内部的节点链接机制

(以 PCB 链表为例)

节点的组成 :每个 PCB 节点除了包含进程的相关信息外,还包含用于链接的指针。这些指针就像是节点之间的 “纽带”,通过它们可以将一个个独立的节点串连起来形成链表。在单链表结构中,每个节点有一个指向下一个节点的指针;在双链表结构中,每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。

当操作系统需要将一个新的进程加入到链表中时,首先会分配一块内存来存储该进程的 PCB。然后,根据链表的组织方式(如在单链表尾部添加、单链表头部添加或者在双链表的某个位置添加)来设置新节点的指针。

对于进程链表的维护,当进程状态发生变化(如从就绪态变为运行态,或者从运行态变为阻塞态等),操作系统可能会将该进程的 PCB 从一个链表移动到另一个链表。这涉及到解除该节点在原链表中的链接关系,并重新建立在新链表中的链接关系

好!看完这些前言补充知识我们继续介绍进程的几种状态

3.什么是状态?

我们可以从实际生活中将这个概念抽象出来,比如说我现在处于写文章的状态,那我应该坐在椅子上用电脑来写,再比如我一会是处于睡觉的状态,那我就应该乖乖的在床上躺着,不能满地乱跑,所以我们可以抽象出:状态,决定了进程接下来要做的工作。

在早期的操作系统设计中,进程状态可以用一个整数来表示,每个整数值对应一种特定的状态。

例如:

0:运行态(Running)

1:就绪态(Ready)

2:阻塞态(Blocked)

3:僵尸态(Zombie)

4:终止态(Terminated)

4.运行态

4.1 运行态的定义

运行态(Running)是指进程正在 CPU 上执行的状态。在单核 CPU 系统中,任何时候只有一个进程处于运行态。在多核 CPU 系统中,每个 CPU 核心可以同时运行一个进程。运行态的进程是当前被调度程序选中并分配了 CPU 时间片的进程。

4.2 运行态的特点

独占 CPU:运行态的进程独占 CPU 时间片,直到时间片用完、被更高优先级的进程抢占,或者因等待某些事件(如 I/O 操作)而主动放弃 CPU。

上下文切换:当进程从运行态切换到其他状态(如就绪态或阻塞态)时,操作系统会保存当前进程的上下文(包括寄存器状态、程序计数器等),并将 CPU 的控制权交给下一个进程。这个过程称为上下文切换。

时间片管理:操作系统为每个运行态的进程分配一个时间片(time slice),时间片的长度由调度算法决定。当时间片用完时,进程会被切换到就绪态,等待下一次调度。

优先级调度:运行态的进程可能因为优先级较低而被更高优先级的进程抢占。调度算法会根据进程的优先级和其他因素决定哪个进程应该获得 CPU 时间片。

5.阻塞态

5.1 阻塞态的定义

 阻塞态(也称为等待态或睡眠态)是操作系统中进程的几种基本状态之一。当进程无法继续执行因为它在等待某个事件(如I/O操作完成、信号量、互斥锁或其他同步机制)时,它就会进入阻塞态

5.2 阻塞态的特点

  1. 等待事件:进程进入阻塞态是因为它们正在等待某个事件的发生。例如,一个进程可能因为等待用户输入或等待磁盘I/O操作完成而进入阻塞态。

  2. 不占用CPU:在阻塞态下,进程不会占用CPU资源,因为它们不能执行任何指令直到等待的事件发生。

  3. 状态转换:当进程等待的事件发生时(例如,I/O操作完成),进程会从阻塞态转换到就绪态,然后可能被调度程序选中,转换到运行态。

5.3 阻塞态的具体示例

假设有一个进程需要从磁盘读取数据,但是磁盘I/O操作需要一些时间来完成。在等待磁盘操作完成期间,该进程可以进入阻塞态。实际上不仅如此,我们写一个伪代码:

while(1)

{

}

上述代码运行时为运行态,而下述伪代码:

while(1)

{

        printf("hehe");

}

该代码为阻塞态,因为我们的计算机一秒钟可以打印上千行的,在这里我仅仅是一个普通的小打印,计算机的大部分时间都是处于阻塞态的状态(这一秒内打印完后停顿,等待下一次打印),但是如果你运气够好,你也可能会看到他的运行态

5.4 阻塞态的管理

操作系统通过进程控制块(PCB)来管理进程的状态。PCB中包含一个状态字段,用于记录进程的当前状态。当进程进入阻塞态时,操作系统会更新PCB中的状态字段,并可能将进程添加到特定的阻塞队列中,以便在相应的事件发生时能够找到并唤醒这些进程。

5.5 阻塞挂起

5.5.1 什么是阻塞挂起

阻塞挂起状态是进程状态管理中的一个重要概念,它涉及到进程在等待某个事件(如I/O操作)完成时的行为,以及在系统内存不足时的处理方式。

当一个进程被暂停执行时,称该进程处于挂起状态。当内存空间较为吃紧时,操作系统会将一些暂时不需要使用的内存数据(如阻塞状态的PCB控制的代码和数据)唤出到磁盘中的swap交换分区。此时等待队列中的PCB不再维护该进程的代码和数据,这样的进程状态叫做阻塞挂起。若设备准备就绪,则操作系统就将swap交换分区中的代码和数据重新唤入到内存中,给PCB维护,然后恢复到运行状态。

附图一张

阻塞挂起通常发生在以下情况:

  1. 等待I/O操作:进程可能因为需要等待磁盘I/O、网络I/O等操作完成而进入阻塞状态。

  2. 等待资源:进程可能因为等待某些资源(如打印机、扫描仪等外设)而进入阻塞状态。

  3. 内存不足:当系统内存不足时,操作系统可能会选择将一些阻塞态的进程挂起,即将它们从内存中移出,放入到磁盘上。

在阻塞挂起状态下,进程的PCB和数据被存储在磁盘上,而不是在内存中。这意味着进程在磁盘上处于非活动状态,直到操作系统需要重新启动该进程。当进程被挂起时,操作系统会更新其状态,以便在适当的时候重新加载它。

5.5.2 阻塞挂起与阻塞态的区别

阻塞态(Blocked State)和阻塞挂起(Blocked or Suspended)虽然都涉及到进程因为等待某个事件而暂停执行,但它们之间存在一些关键的区别:

  1. 内存中的状态

    • 阻塞态:进程仍然保留在内存中,只是暂时不能执行。

    • 阻塞挂起:进程被从内存中移出,放入到磁盘上。

  2. 系统资源的使用

    • 阻塞态:进程虽然不能执行,但仍然占用内存资源。

    • 阻塞挂起:进程不占用内存资源,但占用磁盘空间。

  3. 恢复执行

    • 阻塞态:当等待的事件完成后,进程可以直接从阻塞态转换到就绪态,等待调度执行。

    • 阻塞挂起:当等待的事件完成后,进程需要从磁盘上恢复到内存中,然后才能转换到就绪态.

     4.系统内存管理

            1.阻塞态:通常不需要操作系统进行特殊的内存管理。

            2.阻塞挂起:需要操作系统进行内存和磁盘空间的管理,包括将进程数据写入磁盘和从磁                                     盘恢复到内存。

5.6 阻塞态和运行态的关系

阻止态和运行态是进程状态模型中的两个互补状态。它们之间的关系可以通过以下方式来理解:

1)状态转换:进程可以在阻止态和运行态之间转换。例如,一个进程可能因为等待I/O操作而进入阻止态,当I/O操作完成后,进程转换回就绪态,并可能最终被调度到运行态。

2)资源管理:阻止态和运行态的引入有助于操作系统更有效地管理资源。阻止态允许操作系统挂起等待资源的进程,而运行态则允许操作系统调度和执行那些已经准备好的进程。

3)系统效率:通过合理地管理进程的阻止态和运行态,操作系统可以提高系统的效率和响应性,确保CPU资源得到充分利用,同时满足进程对资源的需求。

换句话讲,就是让进程的task_struct 1)更改task_struct的属性  2)连入不同的队列中

6.Linux的进程状态

首先我们要明确一点,我们上文中提到的阻塞态,运行态等等都是操作系统的状态,而这里我们将针对Linux操作系统的状态进行讲解,Linux操作系统是操作系统的子集,因此Linux操作系统的状态也应该是与操作系统的状态是一种相对应的关系。

6.1   R状态

1)含义:进程正在运行或在运行队列中等待。

2)描述:进程正在使用或等待使用 CPU。这包括进程正在执行或即将执行的情况。

3)特点:进程处于活跃状态,正在被 CPU 调度执行。

6.2  S状态

1)定义:进程处于可中断的睡眠状态。

2)行为:进程在等待某个事件(如 I/O 操作)完成。在这种状态下,进程不会占用 CPU,因为它正在等待外部事件的发生。

3)可中断性:这种状态是可中断的。这意味着如果进程接收到唤醒信号(如 SIGCONT),它可以从睡眠状态被唤醒并返回到就绪状态,准备再次运行。

4)常见场景:进程可能在等待文件读写操作、网络通信或其他需要等待的系统调用完成时进入这种状态。

6.2.1 常见的信号状态(了解)

1. SIGHUP (1):用户从终端挂起或终端关闭时发送。通常用于提示进程重新加载配置文件。

2. SIGINT (2):用户按下 Ctrl+C 时发送。通常用于请求进程终止。

3. SIGQUIT (3):用户按下 Ctrl+\ 时发送。通常用于请求进程终止并生成核心转储。

4. SIGILL (4):进程执行了非法指令时发送。

5. SIGTRAP (5):由 trap 命令或断点指令产生。

6. SIGABRT (6):进程调用 abort() 函数时发送。通常用于请求进程终止并生成核心转储。

7. SIGFPE (8):进程执行了非法的浮点操作时发送。

8. SIGKILL (9):用于立即终止进程。进程无法捕获或忽略此信号。

9. SIGSEGV (11):进程访问了它没有权限的内存区域时发送。

10. SIGPIPE (13):进程尝试向没有读端的管道写入数据时发送。

11. SIGALRM (14):由 alarm() 函数设置的定时器到期时发送。

12. SIGTERM (15):用于请求进程终止。进程可以捕获或忽略此信号,但通常用于优雅地终止进程。

13. SIGUSR1 (10) 和 SIGUSR2 (12):为用户定义的信号。进程可以为这些信号指定自定义的处理程序。

14. SIGCHLD (17):子进程停止或终止时发送给父进程。

15. SIGCONT (18):用于继续一个停止的进程。

16. SIGSTOP (19) 和 SIGTSTP (20):用于停止进程。 SIGSTOP 无法被进程捕获或忽略,而 SIGTSTP 可以被用户(如按下 Ctrl+Z )产生并可以被捕获。

17. SIGTTIN (21) 和 SIGTTOU (22):后台进程尝试读取或写入终端时发送。

6.3 D状态

1)定义:进程处于不可中断的睡眠状态。

2)行为:进程在等待某些特定类型的事件完成,这些事件通常涉及到硬件操作,如磁盘 I/O。在这种状态下,进程同样不会占用 CPU。

3)不可中断性:这种状态是不可中断的。这意味着进程不能通过接收信号来唤醒,它必须等待所等待的事件自然发生。如果进程处于这种状态,它将忽略大多数信号,包括终止信号。

4)常见场景:进程可能在等待磁盘操作完成时进入这种状态。例如,当磁盘驱动器没有响应时,进程可能会进入 D 状态,因为它在等待磁盘操作的结果。

6.3.1S状态和D状态的区别

1)中断性:S 状态是可中断的,而 D 状态是不可中断的。这是两者最根本的区别。换句话讲,S是浅度休眠,对应操作系统的阻塞态,是响应外部事件,D是深度催眠状态

2)唤醒方式:S 状态的进程可以通过接收信号被唤醒,而 D 状态的进程不能被信号唤醒,只能等待它所等待的硬件操作完成。换句话说,D状态不对任何事件响应,OS也杀不掉,只能等进程自己醒来,重启都没用,所以说你的电脑如果近期进程一直要是D的话抓紧备份数据吧~说不定哪天就坏了

3)等待资源类型:S 状态通常与软件资源的等待相关,而 D 状态则与硬件资源的等待相关。等键盘:S,等磁盘:D

4)响应性:S 状态的进程对信号有响应,可以被操作系统或用户中断;D 状态的进程则对信号没有响应,直到等待的硬件操作完成。

6.4 X状态与Z状态

 6.4.1 x状态

X死亡状态(dead):这个状态只是⼀个返回状态,你不会在任务列表里看到这个状态。

就像我们之前学的函数栈帧的创建与销毁一样,我们的进程也是有创建与销毁的,当一个进程结束了,说明其状态已经变为了X,这个进程的资源(代码数据)将会被释放,但是会把task_struct保留,会记录进程退出的时候的推出信息,从而方便父进程读取退出码,让父亲知道自己的儿子死了,让父亲去善后一下,换句话说,进程结束是进程创建的反过程,我们的进程被创建出来,就是为了完成任务的。然而当进程结束的时候,我们需要有一个值反馈,我们这个进程的任务完成的怎么样,这也就阐述了为什么我们C/C++里面为什么int main 之后要return 0而不是return 其他数字的原因,因为不同数字代表着不同的结果,0代表完美的结束了,(这里略作了解就可以,后续会接着讲)。之后我们的系统接收到完美完成的命令后,将内存资源释放。因此,进程在结束之后并不是立即释放该进程的所有资源。进程会先进入僵尸状态(Z)。

6.4.2 Z状态

1)定义

僵尸进程是指子进程已经结束,但父进程还没有回收它的状态信息(通过 wait() 或 waitpid() 等系统调用),此时这个子进程就变成了僵尸进程。但是该进程还在系统进程列表中占着位置

2)危害

僵尸进程会占用系统资源,特别是进程表项等资源。如果系统中产生大量僵尸进程,可能会导致进程表耗尽,从而无法创建新的进程,并且还会内存泄漏。它们还会让系统管理员难以准确判断系统中正常进程的状态,因为僵尸进程在进程列表中显示为 “<defunct>”,可能会引起混淆

 3)处理方法

最好的处理方法是在父进程中调用 wait() 或 waitpid() 等函数来等待子进程结束并回收其状态信息。在编写程序时,应该合理地处理子进程结束的情况,避免产生僵尸进程。如果僵尸进程已经产生,可以通过 kill 命令向父进程发送 SIGCHLD 信号,让父进程回收僵尸进程。如果父进程已经结束,该子进程进程会被 init 进程(进程号为 1)收养,系统的bash会自动回收这些僵尸进程。

6.4.3 孤儿进程

孤儿进程是指其父进程已经终止,但子进程仍在运行的进程。在这种情况下,子进程会被系统中的init进程(进程号为1)收养,并由init进程对它们完成状态收集工作

6.4.4 实例说明 

举个例子,假如有一天我正在公园里面跑步,旁边一个程序员呼啸而过,随后就啪唧的一声摔倒了地上,嘎掉了,我呢是个热心好公民,不会袖手旁观,于是拨打了110,警察们来了之后封锁了现场,叫来了法医还有120,之后120将人拉走了并开了死亡通知书,那么在这个人倒地的一瞬间到被开死亡证明期间,这个人就属于僵尸状态(Z状态),随后,这个人就被立即烧掉了,变成了一堆灰,这时这个人便是X状态,对应的操作系统的释放资源的过程,剩的那一堆灰可以理解为tast_struct,通过这个例子希望大家可以更好的理解X状态与Z状态的关系(仅用于举例,别无它意哈)
 

6.5 T状态

1)含义:暂停状态。

2)描述:进程被暂停执行,通常是因为它接收到暂停信号(如   SIGSTOP   或   SIGTSTP  )。

3) 特点:

               1.进程可以被用户或系统暂停。

               2.暂停状态的进程不会占用 CPU 时间。

               3.可以通过发送继续信号(如   SIGCONT  )来恢复进程的执行。 

6.6 t状态

1)含义:跟踪停止状态。

2)描述:进程被调试器或跟踪工具暂停,通常用于调试目的。

3)特点:

               1.进程被调试器暂停,以便进行代码分析和调试。

               2.停止状态与普通暂停状态类似,但通常用于调试工具的控制。

               3.可以通过调试工具恢复进程的执行。

6.6.1 kill 命令

1)基本命令

kill 命令是 Unix 和 Linux 系统中用于向进程发送信号的标准命令。信号是操作系统用来通知进程发生了某种事件或需要采取某种行动的一种机制。kill 命令可以用于终止进程、暂停进程、继续暂停的进程等。

kill 命令的基本语法如下:

kill [选项] PID
  • PID 是进程标识符,即进程的 ID。

  • [选项] 是可选的信号名称或信号编号。

 示例:

kill -19 1234

向 PID 为 1234 的进程发送 SIGCONT 信号,使其继续执行

2)状态变化

在使用 kill 命令后,进程的状态可能会变为 S(睡眠)、T(停止)或 t(跟踪停止),具体取决于发送的信号类型和进程的响应方式。

进程状态变化总结

  • 睡眠状态(S):进程正在等待某个事件(如 I/O 操作)完成,不会响应 kill 命令。

  • 停止状态(T 或 t):进程被 SIGSTOPSIGTSTP 信号停止,可以通过 SIGCONT 信号恢复。

  • 僵尸状态(Z):进程已经终止,但尚未被其父进程读取其状态信息。

6.6.2 进程的查看

我们可以用ps或者top查看,这里我们以ps为例:

a:显示一个终端所有的进程,包括其他用户的进程。

x:显示没有控制终端的进程,例如后台运行的守护进程。

 j:显示进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相关的信息

 假设我们写了一个process.c文件,其内部只有一个while(1)循环,没有其他内容,我们可以查一下其进程。

当我们输入:

 进程由R变为S

此时也许有细心的小伙伴发现状态后面有一个+,实际上,这个+表示这是一个前台进程,用ctrl+c就可以终止掉 。

http://www.xdnf.cn/news/1726.html

相关文章:

  • [创业之路-384]:企业法务 - 初创公司,如何做好知识产品的风险防范?
  • 质检LIMS系统在金融咨询行业的应用 金融咨询行业的实验室数字化
  • Linux下编译opencv-4.10.0(静态链接库和动态链接库)
  • Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置
  • 2025-04-24 Python深度学习4—— 计算图与动态图机制
  • 极狐GitLab 如何 cherry-pick 变更?
  • STM32移植最新版FATFS
  • Godot开发2D冒险游戏——第二节:主角光环整起来!
  • C# new Bitmap(32043, 32043, PixelFormat.Format32bppArgb)报错:参数无效,如何将图像分块化处理?
  • STM32F103_HAL库+寄存器学习笔记20 - CAN发送中断+ringbuffer + CAN空闲接收中断+接收所有CAN报文+ringbuffer
  • Python爬虫去重策略:增量爬取与历史数据比对
  • VulnHub-DC-2靶机渗透教程
  • zip是 Python 中 `zip` 函数的一个用法
  • 数模学习:一,层次分析法
  • flutter 小知识
  • 在Ubuntu 18.04 和 ROS Melodic 上编译 UFOMap
  • 跨浏览器音频录制:实现兼容的音频捕获与WAV格式生成
  • Spring Security认证流程
  • LabVIEW实现Voronoi图绘制功能
  • 【MQ篇】初识RabbitMQ保证消息可靠性
  • 信息系统项目管理工程师备考计算类真题讲解七
  • KMS工作原理及其安全性分析
  • Java Agent 注入 WebSocket 篇
  • java方法引用
  • kotlin和MVVM的结合使用总结(二)
  • 一种Spark程序运行指标的采集与任务诊断实现方式
  • CE第二次作业
  • NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve
  • Git 的基本概念和使用方式
  • C++跨平台开发要点