Linux操作系统之进程(二):进程状态

目录

前言

一、补充知识点

1、并行与并发

2、时间片

3、 等待的本质

4、挂起

二. 进程的基本状态

三、代码演示

1、R与S

 2、T

3、Z

 四、孤儿进程

总结:


前言

在操作系统中,进程是程序执行的基本单位。每个进程都有自己的状态,这些状态反映了进程在系统中的当前活动情况。理解进程状态对于系统编程、性能调优和问题排查至关重要。今天,我们将深入探讨Linux进程的各种状态,并结合实际例子分析它们的行为。

一、补充知识点

在上文中我们介绍了进程的属性与进程的创建(详细查看上文链接),在了解我们本篇文章的主题——进程状态之前,我们需要给大家先补充几个知识点概念:

1、并行与并发

单核CPU执行进程代码不是把进程代码执行完毕才开始执行下一个,而是给每一个进程预分配一个时间片,基于时间片进行调度轮转(单CPU下),这通常被称为并发。

所谓并发,就是多个进程在一个CPU下采用的进程切换的方式,在一段时间之内让多个进程都得以推进。

那么所谓并行呢?就是多个进程在多个CPU下分别同时进行运行,这称之为并行。

CPU切换和运行的速度非常快,站在我们对应的这个CPU看来呢,当前这一个物理CPU它切换了多个进程,但是因为它切换和运行的速度非常快,所以用户根本就感知不到,

所以这也就解释为什么我们平时的死循环把不会程序卡死的原因,CPU会直接在操作系统的指导下,他会按照时间片来进行我们对应的轮转和调度。

2、时间片

Linux /Windows民用级别的操作系统为分时操作系统

就是我们整个操作系统它在帮你去执行任务时,是会给每一个任务给他分配上对应的一个时间片的,比如说是10毫秒或者是1毫秒。把时间片分好之后,每一个进程在CPU上去运行时,他把自己的时间片耗尽了,他就必须得从CPU上去剥离下来,剥离下来之后再把另一个任务再放上去。这就叫做分时操作系统。没有优先级的谁高谁低,特点:调度任务追求公平,这保证了保证用户操作的及时响应。

实时操作系统通常用于VxWorks、FreeRTOS、QNX(工业/嵌入式领域),主要特点是确定性调度,任务优先级严格分级,高优先级任务可抢占低优先级任务,支持硬实时(Hard Real-Time)和软实时(Soft Real-Time)

  • 硬实时:必须在绝对截止时间内完成(如航天控制系统)

  • 软实时:允许偶尔超时(如视频流处理)

3、 等待的本质

 等待的本质:当进程需要访问外部设备(如键盘、磁盘、网络)时,若设备未就绪,CPU 不会持续轮询等待,而是将该进程移出运行队列,挂入设备的等待队列

操作系统是怎么管理硬件的呢?也是:先描述,再组织

每个硬件设备(如键盘、磁盘)在内核中对应一个 struct device 结构体,包含:

struct device 
{unsigned int id;          // 设备唯一标识enum device_status status;// 设备状态(就绪/忙碌/错误)struct list_head wait_queue; // 等待该设备的进程队列// 其他驱动相关字段...
};

当进程需等待设备时,其 PCB 会被链入设备的 wait_queue(等待队列),直至设备触发中断通知就绪。

 阻塞和运行本质上都是在等待。,只是一个在硬件的等待队列中等待,一个在CPU的运行队列中等待

当从键盘中输入数据后,操作系统会得到信息,随后又将该PCB连入运行队列。

本质上就是把一个PCB一会放在运行队列里,一会放在设备等待队列里,来回的去调用。

4、挂起

内存不足时,操作系统将非活跃进程的代码和数据换出到磁盘(Swap 分区),仅保留 PCB 在内存,这就叫做挂起。

特点是用时间换空间:换入/换出操作增加延迟,但缓解内存压力。

在挂起后,进程变为进程变为阻塞挂起状态,挂起通常是在Swap分区里进行的。在云服务器中通常会禁掉Swap分区,这是为了防止频繁的进行换入与换出操作导致性能骤降。

二. 进程的基本状态

在Struct device中有一个status属性,这个通常记录了当前进程的状态。

我们经常在一些教科书上看见以下图片:

这样的图片肯定是无法让大家清楚的明白什么是进程的状态。 

在Linux中,进程的状态通常可以通过 ps 或 top 命令查看,常见的有:

  • R(Running) 并不意味着进程⼀定在运⾏中,它表明进程要么是在运⾏中要么在运⾏

    队列⾥。
  • S(Sleeping):可中断睡眠(意味着进程在等待事件完成,如I/O)。

  • D(Uninterruptible Sleep):不可中断睡眠(通常涉及硬件操作)。

  • T(Stopped):进程被暂停,可以通过发送 SIGSTOP 信号(如 kill -19给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运⾏。

  • Z(Zombie):僵尸进程(已终止但未被父进程回收)。

  • X(Dead):进程已完全终止(这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。)。

此外,还有一些特殊状态(以下并非全部):

  • t(Tracing stop):进程被调试器暂停(如 gdb 断点)。

  • <(高优先级) 和 N(低优先级):调度优先级相关。

我们通常可以在终端中输入 ps ajx或ps aux来查看:

a:显示⼀个终端所有的进程,包括其他⽤⼾的进程。
x:显示没有控制终端的进程,例如后台运⾏的守护进程。
j:显示进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相关的信息
u:以用户为中心的格式显示进程信息,提供进程的详细信息,如用户、CPU和内存使用情况等

三、代码演示

我们用以下代码与指令操作给大家演示一下进程各种状态的查看:

1、R与S

在当前路径中我们有以下文件:

code.cpp:

#include<stdio.h>
#include<unistd.h>int main()
{int cnt=0;while(1){printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());}return 0;
}

Makefile:

# 定义编译器和编译选项
CXX = g++
CXXFLAGS = -Wall -std=c++11# 定义目标文件和可执行文件名
TARGET = code
SRC = code.cpp# 默认目标
all: $(TARGET)# 直接生成可执行文件(不生成.o文件)
$(TARGET): $(SRC)$(CXX) $(CXXFLAGS) -o $@ $<# 清理生成的文件
clean:rm -f $(TARGET)# 运行程序
run: $(TARGET)./$(TARGET).PHONY: all clean run

首先我们调用make生成可执行文件code(exe),随后输入

./code

运行code可执行文件:

随后在另外一个终端中输入

watch -n 1 '(ps ajx | head -n 1; ps ajx | grep -w "./code" | grep -v grep)'

 查看进程状态:

 这里有两个code进程,第一个进程是bash创建的进程组,用于管理我们在前台运行的code程序,我们不用管它,通过pid 3825121我们可以知道第二个code就是我们运行的程序,可以看见,code的进程状态(就是STAT这一栏),一直在R与S中变换(+号表示 前台进程组,受终端控制,如Ctrl+C能终止它),这是为什么呢?

进程状态切换是由 CPU时间片调度 和 I/O等待 共同作用的结果:

  • R+(Running):进程正在CPU上执行,或位于运行队列等待调度。

  • S+(Sleeping):进程因等待I/O(如printf到终端)被移出运行队列。

我们的code.cpp中有着printf这个函数,这会涉及到IO的相关操作,printf不是直接输出到屏幕,而是写入 标准输出缓冲区,最终通过 终端设备(如/dev/pts/0)显示。又因为终端I/O速度远慢于CPU,因此每次printf都可能触发进程阻塞(进入S状态)。

R → S:当进程调用阻塞式I/O(如printfscanf)。

S → R:当I/O操作完成(如终端准备好接收输出)。

 2、T

依旧是原来几个文件,我们继续运行code:

我们在创建一个终端,输入kill -19 +【对应进程PID】

 此时我们透过之前的查看进程状态的终端可以发现,code进程已经变为了T状态:

 若我们此时在输入kill -18:

 又会发现:

程序又开始跑起来了,与之前不同的是,没有了+号,这是因为被中断后又继续后,进程默认变为了后台进程,此时在进程运行的终端上,输入strl c是不会终止进程的,这个时候就只能通过kill -9来杀死进程:

3、Z

 在讲僵尸状态之前,我们先想一下,一个进程为什么会被创建出来呢?

一个进程会被创建出来是为了完成用户的某个任务,那么操作系统怎么知道这个任务是否完成成功了呢?

我们以前写代码,比如做题,可以通过打印信息来了解,如果打印信息无关呢?

我们更改code.cpp代码如下:

#include<stdio.h>
#include<unistd.h>// int main()
// {
//     int cnt=0;
//     while(1)
//     {
//         printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());
//     }
//     return 0;
// }int main()
{int cnt=0;for(int i=0;i<10;i++){cnt++;}return 0;
}

code不再是一个无限循环代码,重新输入make生成可执行code文件并运行:

 可以看见,如果没有打印信息,我们是无法知道任务完成成功了吗?

请大家在终端上输入:echo $?

 我们再把code.cpp中main函数的返回值设定为11呢?

return 11;

再运行:

我们发现,这次打印出的数又变成11了。细心的同学可能就有所猜测了。

没错,我们每个程序的main函数最后都会有一个return 返回值,当我们重新正常运行结束后,会执行return语句,这个返回的数,就会被父进程接受,告诉父进程,该进程执行任务是否成功。 

我们规定,返回0为执行任务成功,返回非0为失败。

进程退出时:

1、代码不会执行了,首先可以立即释放的就是进程对应的程序信息数据

2、进程退出要有退出信息,保存在自己的task_struct内部

3、管理该进程的task_struct必须被OS维护起来,方便用户未来进行获取进程退出的信息

那么这个跟Z僵尸状态有什么关联呢?大家不要着急,我们把code.cpp更改如下:

#include<stdio.h>
#include<unistd.h>// int main()
// {
//     int cnt=0;
//     while(1)
//     {
//         printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());
//     }
//     return 0;
// }// int main()
// {
//     int cnt=0;
//     for(int i=0;i<10;i++)
//     {
//         cnt++;
//     }
//     return 11;
// }int main()
{pid_t id =fork();if(id==0){//子进程int n=10;while(n--){printf("i am child, pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}else {//父进程while(1){   printf("i am parent, pid:%d\n",getpid());sleep(1);}}return 0;
}

 运行并输入

watch -n 1 '(ps ajx | head -n 1; ps ajx | grep -w "code" | grep -v grep)'

查看状态:

我们可以看见,在子进程未执行完毕时, 二者都是出现S+或者R+的状态,但是当我们子进程执行完毕后,可是父进程没执行完毕,就会出现僵尸状态:

僵尸状态的进程:如果没有人管我,我就会一直僵尸,task_struct会一直消耗内存→造成内存泄漏。

后面我们会讲到:一般需要父进程读取子进程信息,子进程才会自动退出。(调用waitpid),我们这里的代码没有调用waitpid,而是一直在循环,就不会去读取子进程的退出信息,导致子进程一直处于僵尸状态。

语言层面的内存泄漏的问题,如果在常驻的进程中出现,影响比较大:比如杀毒软件。

 四、孤儿进程

刚刚的僵尸进程是子进程退出了,父进程还在。但如果是父进程死掉了,子进程还在呢?

更改code代码如下:
 

#include<stdio.h>
#include<unistd.h>int main()
{pid_t id =fork();if(id==0){//子进程while(1){printf("i am child, pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}else {//父进程while(1){   printf("i am parent, pid:%d\n",getpid());sleep(1);}}return 0;
}

我们在第三个终端(用来输入其他指令kill时所使用的终端),输入kill指令杀死父进程:

我们可以发现:

此时的子进程3868637的父进程已经变为1了。

那么这个1进程是什么呢?

输入指令:

ps -fp 1

这个失去原本父进程的子进程,就被称为孤儿进程。如果父进程先退出了,子进程还在:子进程成为孤儿进程,会被系统领养(一般是systemd,我这台云服务器是属于例外) 。

总结:

本文着重介绍了进程的几个状态,并通过各种代码事例带大家见识了一下状态,并为各位介绍了什么是孤儿进程。这就是本篇博客进程状态的主要内容,希望对各位有所帮助。有疑问可以在评论区提出!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/80789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据技术全景解析:HDFS、HBase、MapReduce 与 Chukwa

大数据技术全景解析&#xff1a;HDFS、HBase、MapReduce 与 Chukwa 在当今这个信息爆炸的时代&#xff0c;大数据已经成为企业竞争力的重要组成部分。从电商的用户行为分析到金融的风险控制&#xff0c;从医疗健康的数据挖掘到智能制造的实时监控&#xff0c;大数据技术无处不…

学习 Android(十一)Service

简介 在 Android 中&#xff0c;Service 是一种无界面的组件&#xff0c;用于在后台执行长期运行或跨进程的任务&#xff0c;如播放音乐、网络下载或与远程服务通信 。Service 可分为“启动型&#xff08;Started&#xff09;”和“绑定型&#xff08;Bound&#xff09;”两大…

投标环节:如何科学、合理地介绍 Elasticsearch 国产化替代方案——Easysearch?

一、Easysearch 定义 Easysearch 是由极限科技&#xff08;INFINI Labs&#xff09;自主研发的分布式搜索型数据库&#xff0c;作为 Elasticsearch 的国产化替代方案&#xff0c;基于 Elasticsearch 7.10.2 开源版本深度优化[1]。 插一句&#xff1a;Elasticsearch 7.10.2 是里…

NVC++ 介绍与使用指南

文章目录 NVC 介绍与使用指南NVC 简介安装 NVC基本使用编译纯 C 程序编译 CUDA C 程序 关键编译选项示例代码使用标准并行算法 (STDPAR)混合 CUDA 和 C 优势与限制优势限制 调试与优化 NVC 介绍与使用指南 NVC 是 NVIDIA 提供的基于 LLVM 的 C 编译器&#xff0c;专为 GPU 加速…

Veo 3 可以生成视频,并附带配乐

谷歌最新的视频生成 AI 模型 Veo 3 可以创建与其生成的剪辑相配的音频。 周二&#xff0c;在谷歌 I/O 2025 开发者大会上&#xff0c;谷歌发布了 Veo 3。该公司声称&#xff0c;这款产品可以生成音效、背景噪音&#xff0c;甚至对话&#xff0c;为其制作的视频增添配乐。谷歌表…

Android本地语音识别引擎深度对比与集成指南:Vosk vs SherpaOnnx

技术选型对比矩阵 对比维度VoskSherpaOnnx核心架构基于Kaldi二次开发ONNX Runtime + K2新一代架构模型格式专用格式(需专用工具转换)ONNX标准格式(跨框架通用)中文识别精度89.2% (TDNN模型)92.7% (Zipformer流式模型)内存占用60-150MB30-80MB迟表现320-500ms180-300ms多线程…

十四、Hive 视图 Lateral View

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月20日 专栏&#xff1a;Hive教程 在Hive中&#xff0c;我们经常需要以不同于原始表结构的方式查看或处理数据。为了简化复杂查询、提供数据抽象&#xff0c;以及处理复杂数据类型&#xff08;如数组或Map&#xff09;&#…

微软开源GraphRAG的使用教程-使用自定义数据测试GraphRAG

微软在今年4月份的时候提出了GraphRAG的概念,然后在上周开源了GraphRAG,Github链接见https://github.com/microsoft/graphrag,截止当前,已有6900+Star。 安装教程 官方推荐使用Python3.10-3.12版本,我使用Python3.10版本安装时,在初始化项目过程中会报错,切换到Python3.…

XXX企业云桌面系统建设技术方案书——基于超融合架构的安全高效云办公平台设计与实施

目录 1. 项目背景与目标1.1 背景分析1.2 建设目标2. 需求分析2.1 功能需求用户规模与场景终端兼容性2.2 非功能需求3. 系统架构设计3.1 总体架构图流程图说明3.2 技术选型对比3.3 网络设计带宽规划公式4. 详细实施方案4.1 分阶段部署计划4.2 桌面模板配置4.3 测试方案性能测试工…

数据直观分析与可视化

数据直观分析与可视化 一、数据的直观分析核心价值 数据的直观分析旨在通过视觉化的方式&#xff0c;帮助人们更直观、更快速地理解数据的特征和模式&#xff0c;从而发现趋势、异常值、分布情况以及变量之间的关系&#xff0c;为决策提供支持。 数据可视化与信息图形、信息可…

Neo4j数据库

Neo4j 是一款专门用来处理复杂关系的数据库。我们可以简单地将它理解为一个“用图结构来管理数据的工具”。与我们常见的&#xff0c;像 Excel 那样用表格&#xff08;行和列&#xff09;来存储数据的传统数据库不同&#xff0c;Neo4j 采用了一种更接近人类思维对现实世界理解的…

Java异常处理全解析:从基础到自定义

目录 &#x1f680;前言&#x1f914;异常的定义与分类&#x1f4af;运行时异常&#x1f4af;编译时异常&#x1f4af;异常的基本处理 &#x1f31f;异常的作用&#x1f427;自定义异常&#x1f4af;自定义运行时异常&#x1f4af;自定义编译时异常 ✍️异常的处理方案&#x1…

Redisson分布式集合原理及应用

Redisson是一个用于Redis的Java客户端&#xff0c;它简化了复杂的数据结构和分布式服务的使用。 适用场景对比 数据结构适用场景优点RList消息队列、任务队列、历史记录分布式共享、阻塞操作、分页查询RMap缓存、配置中心、键值关联数据支持键值对、分布式事务、TTLRSet去重集…

打破次元壁,VR 气象站开启气象学习新姿势​

在教育领域&#xff0c;VR 气象站同样发挥着巨大的作用&#xff0c;为气象教学带来了全新的模式&#xff0c;打破了传统教学的次元壁&#xff0c;让学生们以全新的姿势学习气象知识。​ 在传统的气象教学中&#xff0c;学生们主要通过课本、图片和老师的讲解来学习气象知识。这…

k8s面试题-ingress

场景&#xff1a;我通过deployment更新pod&#xff0c;ingress是怎么把新的请求流量发送到我新的pod的&#xff1f;是怎么监控到我更新的pod的&#xff1f; 在 Kubernetes 中&#xff0c;Ingress 是一种 API 对象&#xff0c;用于管理外部访问到集群内服务的 HTTP 和 HTTPS 路…

RHCE 练习三:架设一台 NFS 服务器

一、题目要求 1、开放 /nfs/shared 目录&#xff0c;供所有用户查询资料 2、开放 /nfs/upload 目录&#xff0c;为 192.168.xxx.0/24 网段主机可以上传目录&#xff0c;并将所有用户及所属的组映射为 nfs-upload,其 UID 和 GID 均为 210 3.将 /home/tom 目录仅共享给 192.16…

【动态导通电阻】GaN HEMT动态导通电阻的精确测量

2023 年 7 月,瑞士洛桑联邦理工学院的 Hongkeng Zhu 和 Elison Matioli 在《IEEE Transactions on Power Electronics》期刊发表了题为《Accurate Measurement of Dynamic ON-Resistance in GaN Transistors at Steady-State》的文章,基于提出的稳态测量方法,研究了氮化镓(…

AI 制作游戏美术素材流程分享(程序员方向粗糙版)

AI 制作游戏美术素材分享(程序员方向粗糙版) 视频讲解: 抖音:https://www.douyin.com/user/self?from_tab_namemain&modal_id7505691614690561295&showTabpost Bilibili: https://www.bilibili.com/video/BV1ojJGzZEve/ 写在最前面: 本方法比较粗糙,只对对美术风…

Java求职面试:互联网大厂技术栈深度解析

文章简述 在这篇文章中&#xff0c;我们将通过一个模拟的面试场景&#xff0c;带你深入了解Java求职面试中可能会遇到的技术栈问题。通过这个故事&#xff0c;你可以学习到相关技术点的具体应用场景和面试技巧。 正文 场景&#xff1a;某互联网大厂的面试现场 面试官&#…

学习日记-day11-5.20

完成目标&#xff1a; comment.java package com.zcr.pojo; import org.hibernate.annotations.GenericGenerator;import javax.persistence.*; //JPA操作表中数据&#xff0c;可以将对应的实体类映射到一张表上Entity(name "t_comment")//表示当前的实体类与哪张表…