详细介绍:【Linux】进程的概念和状态

news/2025/10/27 0:34:08/文章来源:https://www.cnblogs.com/yangykaifa/p/19167663

个人主页:@我要成为c嘎嘎大王

希望这篇文章可以让你有所收获!

一、进程

1.1 进程的概念

在给进程下定义之前,我们先了解一下进程:

我们在编写完代码并运行起来时,在我们的磁盘中会形成一个可执行文件,当我们双击这个可执行文件时(程序时),这个程序会加载到内存中,而这个时候我们不能把它叫做程序了,应该叫做进程。

所以说,只要把程序(运行起来)加载到内存中,就称之为进程。

进程的概念:程序的一个执行实例,正在执行的程序等

1.2 描述进程-PCB

PCB:进程控制块(结构体)
当一个程序加载到内存中,操作系统要为刚刚加载到内存的程序创建一个结构体(PCB),进程信息被放在这个结构体中(PCB),可以理解为PCB是进程的属性的集合。

  • 在Linux操作系统下的PCB是:task_struct
  • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

1.3 task_struct

  • 标示符:描述本进程的唯一标示符,用来区别其他进程
  • 状态:任务状态
  • 优先级:相对于其他进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块指针
  • 上下文数据:进程执行时处理器的寄存器中的数据
  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
  • 其他信息:…

组织进程可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

为什么会存在PCB?

操作系统要进行软硬件的资源管理,要管理资源就要对资源先进行描述后组织,要描述就必须有PCB结构体。

如何理解创建进程?

把一个程序加载到内存中变成进程,操作系统会新增一个PCB,连接到双向链表中。

如何理解删除进程?

在双向链表中删除对应的PCB。

二、查看进程

1、 进程的信息可以通过 ls /proc 系统文件夹查看

2、大多数进程信息同样可以使用ps这些用户级工具来获取

ps aux | head -1 && ps aux | grep XXX
#include
#include
int main()
{while(1){sleep(1);}return 0;
}

三、通过系统调用获取进程标示符

3.1 getpid、getppid

  • 进程id(PID)
  • 父进程id(PPID)
#include 
#include 
#include int main(){printf("pid: %d\n", getpid());printf("ppid: %d\n", getppid());return 0;}

3.2 fork初识

对于fork系统接口以下也将从这四个方面进行介绍。

  • 头文件:<unistd.h>
  • 作用:就是创建一个新的进程,新的进程被称为子进程,原来的进程被称为父进程。
  • 返回值类型pid_t,就是有符号整数,与int相同;如果子进程成功创建,在父进程中fork返回子进程的PID,在子进程中fork返回0;如果子进程没有创建成功就返回-1;
  • 没有参数。

3.2.1 fork调用结果演示

注意上面代码中的i的变换,在 ret > 0中 i 不做变化,在 ret == 0 中 i++。

  • 根据上面的演示可以直观的看到程序同时进入和if和else if;
  • 但是在根据后面打印的 i 会发现,在 ret > 0 的情况下打印的 i 每次都为0,ret == 0 的情况下打印的i每次加1,这两个 i 好像互相之间不影响,好像用的就是两个 i 一样。

对于这两种奇怪的现象在下面详细剖析fork你将会得到答案。

3.2.2 fork剖析

以下将围绕三个问题展开解释fork的工作原理。

fork是干什么的?
fork就是用来创建一个新的进程,完成与当前进程不同的功能;

如果进程创建成功,新的进程被称为子进程,而原来的进程被称为父进程。子进程有自己的PID(如上图所示),也有自己的PPID,其PPID就是原来进程的PID,因为是父进程创建了这个进程。

子进程是突然创建的,那它怎么运行,它有没有代码和数据???

答:是的,子进程是由父进程创建的,它没有自己的代码和数据,所以他要与父进程共有代码;

代码是公用的,那数据是共用的吗???

答:代码是共用的可以理解,代码在运行期间不会被修改吗,但是数据就不一样了,数据是有可能被修改的,比如上图中,根据上面的 i 也能发现子进程中打印的 i 数据和父进程中的好像是独立的,这是不是意味着父进程将自己的数据拷贝一份给进程了???看看man手册中是这么说的。

fork使用的是写实拷贝:当子进程不对数据进行修改的时候就与父进程共用一个数据,当子进程需要对数据进行修改的时候再专门拷贝一份来供子进程进行修改。

通过写实拷贝可以让空间利用效率更高,对于不进行修改的数据不拷贝使得空间不会被大批量浪费。

fork为什么要对子进程和父进程分别返回?

答:返回值不同是为了区分父进程和子进程,让不同的进程流去执行不同的代码;父进程中返回值>0,其实际上返回的是子进程的PID,这样就能让父进程找到子进程的属性信息。

fork是如何进行返回两次的?

以下是fork系统接口的伪代码实现。

在fork系统调用接口返回之前就已经创建好了子进程,所以return ret是两个进程共享的,都会执行,所以两个进程都有返回值,返回值不同。

一个变量是如何存储不同内容的?

如果使用一个变量进行接受fork的返回值;

pid_t ret = fork();

这就会使得id中存储不同的数据,这是怎么做到的???

根据前面所说的子进程对父进程的数据会进行写时拷贝。父进程的返回值会直接写入 ret 变量中,而子进程也有返回值,所以子进程也要写入 ret 变量中,此时操作系统就会对 ret 进行拷贝,再生成一份让子进程进行修改。实际上还是两个不同的变量在存储,而不是同一个ret变量存储了两个不同的数据。

四、进程的状态

操作系统存在着五种状态模型:

  • 新建态:刚刚创建的进程,操作系统还没有把它加入可执行进程组中。
  • 就绪态:进程已经做好准备,只有有机会就会开始执行。
  • 运行态:该进程正在执行。
  • 阻塞态:进程在某些事件发生前不能执行,如I/O操作完成。
  • 退出态:操作系统从可执行进程组中释放出进程,或者自身停止,或者是因为某些原因被取消。

但,我们今天是来看Linux操作系统中具体的进程状态。

下面的状态在kernel源代码里定义:

 /**The task state array is a strange "bitmap" of*reasons to sleep. Thus "running" is zero, and*you can test for combinations of others with*simple bit tests.*/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 */};

4.1 R 可执行状态(runing)

注意:此可执行状态R并非上面的运行态。
进程中的R状态并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里,即可被调度。

操作系统会把进程中R状态的进程全放在调度队列中,方便调度。

创建了一个可执行态进程:

#include
#include
int main()
{while(1);return 0;
}

4.2 S 睡眠状态(sleeping)

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

用代码创建一个睡眠状态的进程:

#include
#include
int main()
{while(1)sleep(10);return 0;
}

S状态是浅度睡眠,随时可以被唤醒,也可以被杀掉。

举个例子:
——小明和小华一起去吃饭,而小华对小明说:“小明,你先去楼下等我,我弄会东西,马上就好”。小明下完楼后就开始等小华——着等待的过程就是睡眠状态。

4.3 D 磁盘休眠状态(Disk sleeping)

可以表示为深度睡眠,该进程不会被杀掉,即使你是操作系统。除非我自动唤醒,才可以恢复。

举个例子:
——在系统有一个进程叫“小张”,磁盘中有一个负责写入数据的东西叫“小陈”。
“小张”要把数据存放到磁盘中,拜托“小陈”来存,由于磁盘中的东西较多,“小陈”尝试进行存储,让“小张”等他,如果存储失败,也会告诉“小张”。而在这个时间段,系统中的正在执行的进程越来越多,最后操作系统看见“小张”“占着茅坑不拉屎”,就把“小张”给踢出去了,之后”小陈“存放数据失败了,找”小张“问这数据是删掉还是再存放一次,然而”小张“已经被操作系统干掉了,”小陈“得不到回响,不知道怎么办,这份数据就丢失了。

为了防止这个情况的发生,操作系统就搞了个D状态。

这种状态(D)的进程杀不死。

4.4 T 停止状态(stopped)

向进程发送SIGSTOP信号,该进程会响应该信号进入暂停状态,
向该进程发送SIGCONT信号,该进程会从暂停状态恢复到可执行状态。

4.5 X 死亡状态(dead) & Z 僵尸状态(zombie)

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

Z 僵尸状态:一个处于僵尸状态的进程,会等待它的父进程或操作系统对它的信息进行读取,之后才会被释放。

举个例子:
——一个人突然死亡,普通人不会对现场进行清理,而是报警等警察和法医对该人进行信息的采集,之后才清理现场。

其中,某人充当的角色是进程、警察和法医充当的角色的父进程或者操作系统。

五、僵尸进程

当一个进程变为僵尸状态的时候,该进程就变成了僵尸进程。

  • 僵尸状态(zombie)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程
  • 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

5.1 通过代码来模拟僵尸状态的进程

#include
#include
int main(){pid_t ret = fork();if(ret == 0){// 子进程int time = 5;while(time--){printf("我是一个子进程,我的PID->%d, 我的PPID->%d\n", getpid(), getppid());sleep(1);}}else if(ret > 0){//父进程while(1){printf("我是一个父进程,我的PID->%d, 我的PPID->%d\n", getpid(), getppid());sleep(1);}}else{printf("创建失败\n");return 1;}return 0;
}

用while :; do ps aux | head -1 && ps aux | grep a.out ; echo "#######################"; sleep 1 ; done来监控进程的状态。

5.2 僵尸进程的危害

父进程不读取,子进程会一直处于Z状态,而且维护僵尸进程本身也是数据维护,也属于进程的基本信息,所以保存在PCB中,所以说僵尸进程一直不退出,PCB一直维护,就会导致内存泄漏问题。

如何避免僵尸进程??

可以用wait方法和waitpid方法避免,后面文章中讲。

六、孤儿进程

在Linux中,进程的关系主要是父子关系。

一对父子进程中的父进程退出了,子进程还在运行,就会形成孤儿进程。
如果没有进程来回收该子进程的信息,那么会变成僵尸状态,会存在内存泄漏的问题。

为了解决这个问题,该子进程会立即被 1 号init进程领养。

6.1 通过代码来模拟孤儿进程

通过查看该子进程的信息,可以得知该进程被1号init进程领养。

完 

希望这篇小小文章可以为你解答疑惑!
若上述文章有什么错误,欢迎各位大佬及时指出,我们共同进步!

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

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

相关文章

Minio外网访问内网上传的预签名url的方法以及报错原因

自己个人的经验总结: 当其他技术配置检查多次都无误,但就是不行的时候,要考虑低级错误的可能: 比如本次 就是 之前 存储桶 的名字改过,但是只改了代码里 获取预签名url的,上传的没有改 导致的结果就是url根本是错的,因…

【ESP32 在线语音】星火大模型

【ESP32接入国产大模型之星火】https://blog.csdn.net/vor234/article/details/140594209

RT-Thread 之互斥量使用

互斥量(Mutex)是 RT-Thread 中用于解决线程间共享资源独占访问的核心 IPC 机制,本文简单介绍了互斥量的API函数和使用示例。一、互斥量概述 互斥量(Mutex)是 RT-Thread 中用于解决线程间共享资源独占访问的核心 I…

20232419 2025-2026-1 《网络与系统攻防技术》实验三实验报告

一、实验内容 1.1 了解恶意软件检测机制,学习免杀原理 1.2 熟悉msfvenom的使用,使用msfvenom中的编码器并尝试生成多种类型的文件 1.3 学习使用veil工具的使用 1.4 尝试进行压缩加壳和加密加壳 1.5 利用C语言shellco…

语义文本理解 BERT - MKT

语义文本理解 BERT 问题 如何区分一个同名的语义名字和物体? A区左边路口的房子 B区右边红绿灯的房子 两个房子含义是不一样的。 从“是什么”升级到“是什么以及在什么情境下”​​,提高了准确性。好的,这个问题…

详细介绍:分布式任务事务框架设计与实现方案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

FM-Fusion 利用rgbd相机 ram-GroundingDINO-sam 重建语义地图 - MKT

FM-Fusion 利用rgbd相机 ram-GroundingDINO-sam 重建语义地图https://arxiv.org/pdf/2402.04555

AI元人文构想系列:从战略能力到价值对话的文明之路

AI元人文构想系列:从战略能力到价值对话的文明之路引言:超越“鹦鹉”与“黑洞”的AI未来 当前人工智能的发展正站在一个十字路口:一边是精于模仿却回避价值矛盾的“鹦鹉AI”,另一边是潜藏于金融、信息和地缘政治领…

Rig 项目深度分析报告

Rig 项目深度分析报告 基于我对这个项目的深入研究,让我为你详细分析 Rig 这个 Rust LLM 框架。 📋 项目概述 Rig 是由 Playgrounds 开发的开源 Rust 库,专门用于构建可扩展、模块化且符合人体工程学的 LLM 驱动应…

事件日志查看Windows安装软件情况

在事件日志中选择应用程序日志筛选事件来源,事件ID号

RT-Thread之创建线程

使用RT-Thread创建线程的一些代码模板。一、静态线程创建 1、thread_task.c文件 #include "thread_task.h" #include "main.h" #include <stdio.h> #include "rtthread.h"/…

cias_voice_plyer_handle.c 解析

#if VOICE_PLAY_BY_UART /**************** * 播报器参数初始化 * * * * **/ void audio_player_param_init() {outside_init_stream(&mp3_player, &mp3_player_end, IOT_AUDIO_PLAY_BUF_SIZE);if (!mp3_…

VirtualBox共享文件夹完全指南:实现Windows与Ubuntu无缝文件共享

VirtualBox共享文件夹完全指南:实现Windows与Ubuntu无缝文件共享 问题背景 在使用VirtualBox运行Ubuntu虚拟机时,经常需要在宿主机(Windows)和虚拟机(Ubuntu)之间传输文件。虽然可以通过USB设备或网络传输,但设…

凭借Ubuntu和i.MX 6ULL开发板构建网络共享

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

WampServer下载安装教程(附安装包,图文并茂) - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【CI130x 离在线】FreeRTOS的流缓冲(StreamBuffer)

FreeRTOS StreamBuffer 详解 概述 StreamBuffer(流缓冲区)是 FreeRTOS 提供的一种轻量级数据流传输机制,用于在任务间或中断与任务间高效传输字节流数据。 基本特性 1. 数据结构字节流存储: 以 FIFO 方式存储字节数…

RT-Thread Nano源码浅析

了解RT-Thread Nano源码构成。一、了解源码 从官方下载到RT-Thread Nano源码目录如下图所示1、bsp文件夹 bsp文件夹里面存放的是板级支持包(board support package),用于存放RT-Thread为各种半导体厂商的评估板写好…

《从 “被动听” 到 “主动学”:课堂听讲助力大学生思维成长》

阅读完三篇文章,我首先想到的就是大一到大二的学习状态变化,突然发现上学期的我可能是刚高考完的原因,脑海中还深深印刻着高中生上课必须要认真听讲的烙印。虽然上课给带手机,但是一看到老师走进教室,总是下意识地…

用AI批量生成产品视频!Python+Google Veo 3.1 API让电商转化率飙升

今天跟大家分享一个超实用的电商运营技巧:如何用Python和Google Veo 3.1 AI,把枯燥的产品图片批量变成生动的营销视频。用户原创内容(UGC)现在特别火,对销售的拉动效果非常明显,有了这个工具,你也能轻松制作大量…

关于SQLite - 世界上装机量最多的数据库

关于SQLite - 世界上装机量最多的数据库? 使用C语言开发,使得它小巧精致而高效,直接采用偏底层的语言,使用文件的逻辑,实现SQL数据库的逻辑; 使用方:包括但不限于 Python、Java、C# 等; 无服务器的,…