我与C++的爱恋:进程状态(RSDT 阻塞 僵尸 孤儿)


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🔥个人主页guoguoqiang. 🔥专栏Linux的学习

Alt


在这里插入图片描述

进程状态

在 Linux 中,进程通常有以下三种状态:
1.就绪状态(Ready):

进程已经准备好可以运行,只等待 CPU 时间片的分配。它需要的资源(如内存)都已经准备好,进程仅仅是等待调度器将其从就绪队列中挑选出来并分配 CPU 执行。
2.运行状态(Running):

进程正在实际使用 CPU 执行其代码。在这个状态下,进程正在进行计算、处理数据或执行其他指令。
3.阻塞状态(Blocked):

进程因为等待某些事件(如 I/O 操作完成或获取特定资源)而无法继续执行。在阻塞状态下,进程不会占用 CPU 时间,而是等待条件满足后才能转回就绪状态。

这三种状态是进程调度和管理的核心。为了确保系统能够高效地管理进程,操作系统会在这些状态之间进行转换。这些转换的规则和机制通常由操作系统的调度算法和内核实现

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在
Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:

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

1.运行(Running,R):进程正在 CPU 上运行或者在就绪队列中等待执行。

2.睡眠(Sleeping,S):进程等待某个事件发生(如 I/O 操作完成)或某些资源变得可用。此状态可以进一步分为:

可中断睡眠(Interruptible Sleep,S):进程可以被信号唤醒。
不可中断睡眠(UninterruptibleSleep,D):进程在等待一个不可中断的事件(如磁盘 I/O),不能被信号打断。

3.停止(Stopped,T):进程被暂停执行。通常这是因为进程接收到一个信号(如 SIGSTOP 或 SIGTSTP),或者调试器正在调试该进程。

4.僵尸(Zombie,Z):进程已经终止,但其父进程尚未调用 wait() 来读取其退出状态。僵尸进程保留了一些信息(如退出状态),直到父进程读取这些信息后才会完全清理。

5.脱离(Orphan,O):这是一个特定的状态,指的是父进程已经结束,而子进程依然存在。它通常会被 init 进程收养。

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

每个状态代表了进程在操作系统中的不同生命周期阶段。

进程状态实质是结构体task_struct内部的一个属性。通过宏定义的方式进行描述以及更改进程状态

#define RUN 1
#define SLEEP 2  
#define STOP 3
 
struct task_struct
{
    // 内部的一个属性
    int status;
}
 
struct task_struct process1; //创建进程
process1.status=RUN;//设置进程状态

进程状态的查看

 while :; do ps axj | head -1 && ps axj | grep testStatus | grep -v grep; sleep 1;done 

R和S运行状态

R运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

S睡眠状态(sleeping)意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)

通过C语言代码进行验证
makefile中的代码

bin=testStatus
src=testStatus.c
$(bin):$(src)
        gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
        rm -f $(bin)

testStatus.c代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
 
int main()
{
	while (1)
	{
		printf("I am a process,pid: %d\n", getpid());
	}
	return 0;
}

在这里插入图片描述
进程状态: S+表示前台运行 S表示后台运行
通过运行结果我们i可以看到,我们的进程一直在运行,但是进程状态是S+状态,也就是休眠状态,可是按照我们自己的理解,程序正在运行不应该是R(运行状态)吗?为什么这里是S+状态呢?
这是因为:printf的本质实际上是往显示器上面打印,然而程序运行实在云服务器上的,而最后打印是在我们本地的显示器上的,根据冯诺依曼体系结构,显示器是一个外设,所以CPU在跑的过程中就是先把数据写入内存,然后再刷新到外设上,然后CPU速度比刷新到显示屏的速度快得多,,所以进程在被调度的时候,要访问显示器的资源,因为资源要一直在显示器上打,所以大部分时间,我们的进程一直在等待我们的设备资源是否准备就绪。所以也就是为什么总是S(休眠状态)。
而我们当不在显示器直接打印的时候状态改为R+
在这里插入图片描述

注意:
./testStatus & # 在执行可执行程序后面加&符号,为在后台运行,跑起来后面不带+号。+号表示在前台还是后台运行,后台运行后面有数字(pid),且ctrl +c 不能中断进程,需用kill -9 pid杀掉进程。
在这里插入图片描述
程序在后台运行,只能使用kill -9 pid来杀掉进程
在这里插入图片描述

T/t 暂停状态

kill命令

	kill  [-s <信号名称或者编号>][程序]kill  [-l <信息编号>]

功能:给指定命令发信号

常见选项:-l <信息编号> : 若不加<信息编号>选项,则 -l 参数会列出全部的信息名称。

在这里插入图片描述
总共有64个信息编号,其实上面的这些东西都是宏,我们已经会使用 -9 杀死进程,下面我们会用到 -18(SIGCONT)进程继续 以及 -19(SIGSTOP)暂停进程

一个进程被暂停也会自动变为后台(只能使用kill -9杀死进程)
在这里插入图片描述
在这里插入图片描述
t (tracing stop) : 当前的进程因为被追踪而暂停了

这种情况可以在调试代码的时候遇见,因为调试的过程,中间就有暂停的动作
在这里插入图片描述
在这里插入图片描述
当我们打开gdb模式打断点并执行,gbd的状态为S+,而我们程序的状态变为t,等待下一步调试。

D(disk sleep)状态

D (disk sleep): Linux系统比较特有的一种进程状态,不可被杀,深度睡眠,不可中断睡眠.一个进程。

一个进程要将一个G的数据存储到磁盘,根据冯诺依曼体系结构可以知道,本质是把数据从内存交给外设,由于速度差,进程需要等待到数据写入磁盘,此时进程为S状态,又因为操作系统管理进程,当系统整个的内存资源严重不足时,Linux操作系统有权利杀掉进程来释放空间,然后操作系统把A进程杀掉,但是此时B进程要给磁盘写入数据,硬盘需要去照顾B进程,导致A进程写入数据失败了,1GB的数据丢失了,如果这个数据非常重要,那么可能会造成很大的影响。
◉ 为了避免这种情况,如果进程在等待硬盘资源时,进程需要将自己的状态设为D状态:不可被杀深度睡眠,不可中断睡眠。

◉ 我们一般不会遇到这种情况,从事系统管理、运维、存储等工作可能会遇到。

在这里插入图片描述

杀死D状态的方法:

  1. 让进程自己醒来

  2. 重启,重启不行则断电

Z状态(僵尸状态)

僵尸状态:当进程退出,进程一般不会直接进入x状态(死亡,资源可以立马被回收),而是进入Z状态。如果父进程不对其进行回收,则会一直处于僵尸状态,也就是子进程先于父进程退出。
就好比如:在杀人案中,一名尸体躺在地下,这时候你发现了,你连忙开始报警,报警后,警察来的第一件事不是移走尸体,而是进行封锁现场,让尸体原地待着,等待法医进行检验后,再进行移动。在这个让尸体原地待着的过程,就是Z状态,僵尸状态

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
        return 1;
    } else if (id == 0) { // 子进程
        printf("子进程[%d]开始运行...\n", getpid());
        sleep(5);
        printf("子进程[%d]退出...\n", getpid());
        exit(0);
    } else { // 父进程
        printf("父进程[%d]正在睡眠...\n", getpid());
        sleep(15); // 父进程延迟回收子进程
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述
父进程在休眠,子进程已经结束,子进程未被父进程回收,直到父进程结束休眠,子进程才被回收

为什么父进程不回收子进程PCB?

1.父进程要知道子进程为什么要退出,父进程要获取子进程退出的信息。

2.独立性,父进程只读取子进程的信息,不对信息做修改,依旧保持独立性。

僵尸进程的危害

僵尸进程虽然不再运行,但它们仍然占用系统资源(如进程控制块task_struct)。如果父进程不及时回收子进程,会导致系统资源浪费,甚至内存泄漏。

避免僵尸进程

可以通过以下方式:

  1. 父进程及时调用wait()或waitpid()回收子进程。

  2. 使用信号处理机制,在子进程退出时通知父进程进行回收。

孤儿进程

孤儿进程:父进程不是休眠,而是比子进程提前结束,造成子进程不归原父进程管理,原父进程变为1号进程,相当于被OS领养了
在这里插入图片描述
子进程后来被1号进程领养
为什么父进程退出的时候父亲没有变成僵尸状态呢?为什么我们没有看到父进程变成Z呢?
这是因为父进程的父进程是bash,他会自动回收它的子进程。父进程被bash回收的很快,z->x状态转换的很快所以没看到。
◉ 那为什么刚才我自己代码中的父进程创建的子进程,父进程没有回收子进程呢?那是因为你的代码压根就没有写回收,所以你的子进程就没有回收。

并且这里我们也发现 子进程由 S+ – > S,此刻退出子进程也必须使用kill - 9;

运行状态

每个task_struct对应一个时间片,当执行程序时间到了之后,会自动切换到下一个进程,也就是CPU的执行是基于时间片进行轮转调度的,在一个时间段内同时得以推进代码,就叫做并发
在这里插入图片描述

如果我们不止一个CPU,那么任何时刻,都同时有多个进程在真的同时运行,我们叫做并行
在这里插入图片描述

阻塞状态

在操作系统中,阻塞状态(Blocked State)是进程生命周期中的一个状态,当进程等待某些事件发生而无法继续执行时,它就会进入阻塞状态。
关于进程: ① 一个进程使用资源的时候,可不仅仅是在申请 CPU 资源 ② 进程可能会申请其它资源:磁盘、网卡、显卡,显示器资源…… 如果我们申请 CPU 资源无法暂时无法得到满足,这就需要排队的 “运行队列” 。那么如果我们申请其他慢设备的资源呢?是需要排队的(task_struct 在进程排队)。

当访问某些资源(磁盘,网卡等),如果该资源暂时没有准备好,或者正在给其他进程提供服务,那么此时:

① 当前进程要从 runqueue运行队列 中逐出。

② 将当前进程放入对应设备的描述结构体中的waitqueue等待队列 。

**注意:**并不是只有等待硬件资源的进程才处于阻塞状态,一个进程等待另一个进程就绪、一个进程等待软件资源就绪等也是阻塞状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值