一、进程:操作系统调度的最小单元

1. 进程是什么?

2. PCB与struct task_struct:进程的身份证

(1)PCB基本概念

(2)Linux下的struct task_struct

3. 上下文数据:进程切换的关键

(1)什么是时间片?

(2)什么是上下文数据?

(3)为什么必须要有上下文数据?

(4)上下文的保存与恢复

(5)上下文的意义与开销

四、全文总结


一、进程:操作系统调度的最小单元

1. 进程是什么?

进程是正在运行的程序实例。我们要区分程序与进程的区别:

程序 :程序是存放在硬盘上的、静态的、编译好的可执行文件,它是进程在运行前的“胚胎”状态。

进程 加载到内存中的程序代码与数据 + 程序运行所需的全部管理信息

其中所有管理信息,全部由内核结构体 struct task_struct(PCB)进行封装保存;操作系统会将系统内所有进程的task_struct结构体,以双向循环链表的形式串联起来,形成进程链表。

直白理解:进程链表相当于操作系统的进程花名册。程序的代码和数据分散存储在内存各处,而每一个进程的管控信息,全部串联在这张双向链表中;内核无需遍历零散的内存代码,仅通过遍历这张进程链表,就能统一完成对所有进程的管理、检索与CPU调度。

2. PCB与struct task_struct:进程的身份证

操作系统如何管理成百上千个进程?它无法直接识别二进制代码,所以内核为每一个进程创建一个专属的数据结构,也就是PCB(进程控制块)

(1)PCB基本概念

PCB是操作系统内核用于描述、管理进程的核心数据结构,相当于进程的「档案袋」,进程的所有信息都会存入PCB。只要进程存在,PCB就不会销毁,操作系统通过遍历PCB,完成进程调度、资源分配。

(2)Linux下的struct task_struct

不同操作系统的PCB实现不同,Linux内核中,PCB的本质就是 struct task_struct 结构体,这个结构体包含上百个成员,完整记录进程所有属性,我整理了最核心的常用字段:

  • 进程标识信息:pid(进程唯一ID)、ppid(父进程ID),用于区分不同进程;

  • 进程状态信息:运行态、阻塞态、就绪态、终止态,标记进程当前工作状态;

  • 优先级:决定CPU调度顺序,优先级高的进程优先占用CPU资源;

  • 内存指针:指向进程占用的内存空间,记录代码、数据存放位置;

  • 上下文数据:保存CPU寄存器数据,是进程切换的核心依据;

  • 文件描述符表:记录进程打开的文件、网络套接字;

  • 信号处理信息:管控进程的信号接收、处理逻辑,比如强制结束进程指令;

  • 进程记账信息:记录进程占用CPU时长、内存大小,用于资源统计。

简单来说:Linux内核眼中没有进程,只有一个个task_struct结构体,所有进程管理操作,本质都是对结构体的增删改查。

3. 上下文数据:进程切换的关键

这里我们重点补齐两个核心疑问:什么是时间片?为什么一定要有上下文数据?同时详解进程切换的底层逻辑。

(1)什么是时间片?

时间片(Time Slice)是操作系统CPU调度器分配给单个进程的最小CPU执行时间段

在单核心CPU下,同一时刻只能运行一个进程。为了让用户感觉电脑可以同时运行多个程序,操作系统采用时间片轮转调度算法

  • 时间片时长极短,通常为几毫秒;

  • 系统给链表中每一个就绪进程,公平分配一小段CPU执行时间;

  • 一旦时间片耗尽,无论程序是否执行完毕,CPU都会强制剥夺当前进程的执行权限,切换下一个进程。

通俗理解:CPU就是一座单人独木桥,所有进程排队过桥,操作系统规定每个人只能走5毫秒,时间一到强制换下一个人。因为切换速度极快,人眼无法感知延迟,造成了“多个程序同时运行”的错觉。

(2)什么是上下文数据?

CPU内置大量高速寄存器,寄存器读写速度远高于内存,专门用来临时存放正在执行的指令与运算数据。进程上下文数据,是进程在CPU运行期间,留存于寄存器内的全部临时运行现场数据

举个例子:进程A执行至中途、还未结束时,寄存器中保存着当前运算中间结果、下一条指令地址、堆栈状态等专属数据,这一组记录运行现场的数据,就是进程A的上下文。

(3)为什么必须要有上下文数据?

结合上面的时间片概念,我们可以直白得出结论:上下文数据是时间片轮转调度的必备前提

因为时间片极短,进程永远无法一次性跑完,执行中途一定会被CPU强制切下。如果没有上下文保存机制:

  • 进程被切下时,CPU寄存器的数据会被新进程覆盖;

  • 下次该进程重新上CPU时,丢失了上次运行记录,只能从头执行;

  • 所有程序无法持续运行,电脑彻底瘫痪。

所以,上下文的本质就是保存进程的运行现场,保证进程多次轮转CPU时,能够无缝衔接、接着上次的代码继续执行。

(4)上下文的保存与恢复
  1. 保存上下文:将CPU寄存器中进程A的所有数据,拷贝存入A程序的task_struct结构体中;

  2. 恢复上下文:把进程A的上下文数据,从task_struct中读取出来,写入CPU寄存器,让进程A继续执行。

(5)上下文的意义与开销

上下文数据保证了进程切换不会丢失运行进度,让每一个进程都觉得自己独占CPU资源。我们日常一边听歌、一边打字、一边浏览网页,无感切换多个程序,底层全部依赖上下文的保存与恢复。

补充知识点:频繁切换进程会产生上下文开销,占用CPU资源,所以进程数量过多时,电脑会出现卡顿现象。

四、全文总结

        本文全程围绕进程展开,核心逻辑总结如下:程序是存放于硬盘的静态文件,当程序加载至内存后就成为进程,进程的专业定义为:加载到内存中的程序代码与数据 + 程序运行所需的全部管理信息。Linux内核依靠 struct task_struct(PCB进程控制块)记录进程所有信息,并将所有进程以链表的方式串联,以此统一管理调度进程。为实现多程序并发执行,CPU采用时间片轮转调度算法,给进程分配极短的执行时间,强制轮换执行;而上下文数据是进程留存于CPU寄存器中的运行现场,是进程切换的核心。进程切换时需完成上下文的保存与恢复,保障进程接续执行,频繁切换会产生上下文开销,造成设备卡顿。总而言之,Linux通过结构体描述进程、链表组织进程、时间片轮转调度进程、上下文保留运行现场,最终实现多进程平稳并发运行。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐