【Linux】进程概念

其他篇章【Linux专栏】

其他篇章 【C语言专栏】

上期回顾 【Linux】冯诺依曼---操作系统

文章目录

  • 1.进程的概念
  • [2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct](#2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct)
  • [3. 进程管理](#3. 进程管理)
    • [3. 1组织进程](#3. 1组织进程)
    • [3.2 查看进程](#3.2 查看进程)
    • [3.3 通过系统调⽤获取进程标⽰符](#3.3 通过系统调⽤获取进程标⽰符)
  • [4. 创建进程](#4. 创建进程)
    • [4.1 fork 创建进程](#4.1 fork 创建进程)
    • [4.2 fork的底层原理](#4.2 fork的底层原理)
  • [5. 进程状态](#5. 进程状态)

1.进程的概念

  • 基本概念:程序的一个执行实例,正在执行的程序等。
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。
  • 我们来理解:进程 = 内核数据结构(task_struct) + 自己的程序代码和数据

注意:

  • 运行起来的程序,加载到内存的程序都是进程
  • 一个任务就是一个进程,一个程序可以启动多个进程,程序只有一个,但是一个程序可以有多个进程

2. 描述进程PCB---Linux 操作系统下的 PCB 是: task_struct

2.1基本概念

  • 进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课本上称之为PCB(process control block), Linux 操作系统下的 PCB 是: task_struct

帮助理解:可以把进程理解为老师管理学生,要想管理好学生,老师就得了解学生的基本情况(姓名,学号...),所以OS要想管理进程,就需要一个"信息表",即 PCB (Process Control Block->进程控制块)

2.2task_struct

  • 在 Linux 中描述进程的结构体叫做 task_struct 。
  • task_struct 是 Linux 内核的⼀种数据结构类型,它会被装载到RAM(内存)⾥并且包含着进程的信息。

2.21 task_struct 内容分类

  • 标示符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。

  • 状态: 任务状态,退出代码,退出信号等。

  • 优先级: 相对于其他进程的优先级。

  • 程序计数器: 程序中即将被执⾏的下⼀条指令的__地址__。

  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

  • 上下⽂数据: 进程执⾏时处理器的寄存器中的数据休学例⼦,要加图CPU,寄存器

  • I/O状态信息: 包括显⽰的I/O请求,分配给进程的I/O设备和被进程使⽤的⽂件列表。

  • 记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。

3. 进程管理

3. 1组织进程

Linux内核中, 最基本的组织进程task_struct的方式是采用"双向链表组织"。

3.2 查看进程

1.进程的信息可以通过 /proc 系统文件夹查看
2.大多数进程信息同样可以使用top和ps这些用户级工具来获取

3.3 通过系统调⽤获取进程标⽰符

  • 进程id(PID)
  • ⽗进程id(PPID)
#include 复制代码
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}

4. 创建进程

4.1 fork 创建进程

fork是Linux最基础的进程创建方式(子进程)

1. 本质:复制父进程,创建子进程

  • 调用 fork() 后,原来的进程(父进程)会多出一个几乎完全相同的子进程。代码、数据、内存状态基本都复制一份(只有部分会被修改)。

2.返回值区分父子

  • 父进程: fork 返回子进程 PID(大于0的整数)
  • 子进程: fork 返回 0
  • 出错:返回 -1

3.各自独立运行

两个进程有独立的地址空间,后续修改变量互不影响。

4. 典型用途

  • 让程序同时干多件事(并发)
  • 守护进程、后台服务
  • shell 执行命令时,都是先 fork 再 exec

4.2 fork的底层原理

本质: fork 本质是"内核"帮你复制进程描述信息 + 共享物理内存(写时复制),不是真的全量拷贝内存

内核做了哪几件事?

1.创建 task_struct内核为新进程分配一个task_struct (进程控制块 PCB),把父进程的几乎所有信息复制一份:

  • 页表
  • 文件描述符表
  • 信号处理方式
  • 当前寄存器上下文
  • 内存地址空间结构

2.分配新 PID

  • 子进程有独立 PID,与父进程不同
    (即父子进程地址空间独立,但物理内存初期共享)

3.写时复制(Copy-On-Write, COW)这是 fork 高效的关键:​

  • 刚 fork 完,父子共用同一块物理内存
  • 页表标记为"只读"
  • 谁要修改数据,才触发缺页异常,内核真正复制那一页内存→ 只有修改时才拷贝,不修改就一直共享

4.设置返回值内核分别给父子进程设置不同返回值:​

  • 父进程:返回子进程 PID(大于0的整数)
  • 子进程:返回 0之后两者完全独立调度、独立运行

1.为什么fork()函数要给子进程返回0?给父进程返回子进程pid?

  • 为了后面在调用fork()函数之后,可以根据不同的if判断来让父子进程执行不同的代码片段,及为了区分不同的执行流,使其可以执行不同的代码块。
    (一般而言,fork()函数之后的代码是 父子共享 的!)

2.为什么同一个变量会返回两次呢?

  • 底层是因为当该函数出现了return,创建子进程的时候,父进程共享了return,所以相应的就会出现返回两次的情况

两个返回值的含义

  • 父进程中,fork () 的返回值 = 子进程的 PID(正数):父进程通过这个返回值,识别自己创建的子进程

  • 子进程中,fork () 的返回值 = 0(零):子进程通过返回值 0,识别自己是子进程;

  • 若 fork 返回负数(\u003C0):表示子进程创建失败(比如系统资源不足,无法分配新的 task_struct)。

5. 进程状态

5.1 操作系统(OS)层面进程的状态

调用fork()函数创建子进程之后,父子进程并非一直处于运行状态,会在不同状态之间切换;
(进程状态反映进程执行过程的变化)

  • 三态模型中:进程状态分为 运行态、就绪态、阻塞态;
  • 五态模型中:进程状态分为 新建态、终止态、运行态、就绪态、阻塞态;
  • 运行状态

    进程占有处理器正在运行或正处于运行队列中。

  • 阻塞状态

    进程不具备运行条件,正在等待某个事件的完成。

  • 挂起状态

    进程的挂起状态是指计算机系统中,一个进程因为某些原因而暂时不能继续执行,但仍然保持在进程表中,并且有可能在将来恢复执行的状态。

5.2 Linux系统层面进程的状态

R运⾏状态(running)

  • 并不意味着进程⼀定在运⾏中,它表明进程要么是在运⾏中要么在运⾏队列⾥。

S睡眠状态(sleeping)

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

  • S 状态也叫 "浅度睡眠",是 Linux 中最常见的状态(比如我们打开的浏览器、QQ,大部分时间都处于 S 状态,只有当我们操作时,才会切换到 R 态)。

D磁盘休眠状态(Disk sleep)

  • 有时候也叫 不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待I/O的结束。

  • D 状态是 "保护态"------ 比如进程正在向磁盘写入数据,如果此时强行中断进程,会导致磁盘数据损坏,所以 Linux 内核禁止中断 D 状态的进程。

T停⽌状态(stopped)

  • 可以通过发送 SIGSTOP 信号给进程来停⽌(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运⾏。

X死亡状态(dead)---很少见

  • 这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。

Z僵尸状态(Zombies)

  • 子进程已经退出,但父进程没有读取子进程的退出状态(比如父进程一直在睡眠,没有调用 wait 函数),此时子进程的 task_struct(PCB)不会被释放,处于 "僵尸" 状态。

  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程就会进入Z状态

  • 处于僵死状态的进程就被成为僵尸进程,其相关资源尤其是task_struct结构体不能被释放,这也就会导致僵尸进程会一直占用内存资源!

相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
Yana.nice5 小时前
Linux 只保留 30 天内日志(find命令删除日志文件)
linux·运维·chrome
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117768 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao8 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
吳所畏惧8 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
DFT计算杂谈8 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习