【目标】

  • 分清楚并发和并行的区别,简单掌握关键点
  • 分清楚线程和进程的区别
  • 掌握一些基本概念,了解背景

1.进程和线程的关系

1.1 进程?线程?

进程(Process) 和线程(Thread) 是操作系统里两个最核心的概念。简单说:进程是资源分配的基本单位,线程是 CPU 调度的基本单位。

1.1.1 进程

进程是一个正在运行的程序实例。当你双击一个可执行文件,操作系统会为它创建一个进程,分配独立的内存空间(代码段、数据段、堆、栈)、文件描述符、PID 等资源。

关键特征:

  • 独立地址空间:每个进程有自己独立的虚拟内存空间,一个进程崩溃通常不会影响另一个进程。

  • 资源隔离:进程间通信(IPC)需要借助管道、消息队列、共享内存、socket 等机制,成本较高。

  • 切换开销大:进程切换涉及页表切换、TLB 刷新、上下文保存,开销明显大于线程切换。

1.1.2 线程

线程是进程内部的执行流,一个进程可以包含多个线程。同一进程内的线程共享该进程的地址空间、全局变量、打开的文件等资源,但每个线程有自己独立的栈、寄存器状态、程序计数器。

关键特征:

  • 共享地址空间:线程间通信直接读写共享内存即可,非常高效,但也需要同步机制(锁、信号量)防止数据竞争。

  • 切换开销小:同一进程内线程切换不需要切换页表,开销远小于进程切换。

  • 一个线程崩溃可能拖垮整个进程:因为共享地址空间,某个线程的非法访问可能导致整个进程终止。

现代电脑都是多核电脑,更有超线程(大单核中有两个逻辑处理器,相当于两个小单核)的设计。比如一台具备12个逻辑处理器的电脑,电脑上成千上万的线程都是被操作系统分配到这12个逻辑处理器上面执行的。而CPU调度的最小单位就是线程。

1.1.3 对比总结

维度进程线程
定义资源分配的基本单位CPU 调度的基本单位
地址空间独立同进程内共享
通信方式IPC(管道、共享内存、socket 等)直接读写共享内存
切换开销大小
隔离性强弱
创建开销大小

进程是“资源+隔离”的边界:操作系统分配资源(各个进程之间的资源分配独立)、多个不同程序(比如QQ、微信)之间的隔离;

线程是实实在在“执行+调度”的实体:对于CPU来说,它只认线程,只是线程必须活在某个进程的资源环境里。

1.2 空转?调度?并发?并行?

1.2.1 空转?调度?

一、空转

简单介绍:空转——CPU 的某个执行单元本该在算指令,但因为等不到数据/被阻塞,只能闲着。

二、调度

1.调度机制的关键点

调度 = 决定"谁上 CPU、跑多久、何时换",并执行换人的机制。

关键包含以下四点:

  • 就绪队列:线程就绪后排队等被选中,这是"有货可补"的候选池。
  • 调度器 + 策略:按时间片、优先级、公平性(CFS)等规则,从队列里挑下一个线程。
  • 触发点:时间片用完、线程阻塞、被唤醒、被更高优先级抢占——这些时刻触发调度。
  • 上下文切换:决策完"换谁",保存旧线程上下文、恢复新线程上下文,真正完成换人。

一句话:就绪队列给候选,调度器做决策,触发点定时机,上下文切换执行换人。

1.2.2 并发?并行?

 一、定义

并发(Concurrency):系统里存在多个可独立推进的执行流,它们在一段时间内“交错地都向前推进”。任一时刻,可能只有一个在执行(单核发挥作用),也可能多个在执行(多核发挥作用)——但"交错推进、整体都前进"这个性质成立,就是并发。

并行(Parallelism):多个执行流在“同一时刻真正同时执行”。同一瞬间,有多个执行单元各自在跑指令。

两个定义的分水岭只有一个:看"同一瞬间"有没有多个执行单元在同时干活。

二、它们到底是什么"东西"

都不是东西,不是实体,不是对象。

它们是对"多个执行流在时间上是什么关系"的描述——是运行过程的性质,不是被管理的对象。

用类比:
"拥堵" 不是一辆车、不是一个物体,而是"很多车在路上、彼此拖慢"这个“状态/过程”。你能观察到它(看车流),但抓不到它本身。
"并发/并行" 同理:是"多个执行流以什么时间关系在跑"的性质。**并发 = 交错推进的性质;并行 = 同时执行的性质。

三、两者的精确区别
维度并发并行
时间关系一段时间内交错推进同一瞬间同时执行
是否要求多执行单元不要求,单核即可要求,必须 ≥2 个执行单元
本质逻辑同时性(宏观像同时)物理同时性(微观真同时)
解决的问题等待时不空转、多任务协调提高吞吐、缩短计算时间

关键一句:并发是"结构+调度"造出的逻辑同时性;并行是"硬件"提供的物理同时性。

四、宏观上,并发对应到底层是什么

并发的底层支撑分三层:

1. 结构层:多个执行流共存
程序里有多个线程(或协程、进程),每个都有独立的执行上下文(栈、寄存器状态、程序计数器)。这是并发的前提——得先有"多个可推进的东西"。

2. 机制层:OS 调度器 + 时间片 + 上下文切换

  • 调度器维护“就绪队列”,把就绪线程分派到逻辑处理器。
  • 时间片轮转:一个线程跑一段(时间片),时钟中断触发,换下一个。
  • 阻塞让出:线程等 I/O 时进阻塞态,让出执行单元,调度器补位。

这三样合起来,让"多个执行流在一段时间内都前进"成为现实。

3. 硬件层
单核上任一时刻只有一个线程在跑,并发完全靠“操作系统”制造交错。这就是为什么并发不依赖多核。

并发不空转的完整链条:
> 多线程共存(前提)→ 线程 A 阻塞,让出执行单元(腾位置)→ 内核触发调度 → 调度器从就绪队列选 B 补位(动作)→ 执行单元不空转。
> 并发只提供"有货可补"的前提;补位是调度的活;就绪队列为空时,并发在,照样空转。

五、宏观上,并行对应到底层是什么

并行的底层支撑是“硬件的同时执行能力”,分四个层次:

1. 多物理核
N 个物理核,同一时刻 N 个线程各自在自己的核上执行指令。这是最典型的并行,物理上同时导通。

2. 超线程(有限并行)
一个物理核暴露为两个逻辑处理器,各有独立寄存器组,但共享执行单元。

3. 核内微架构并行(单核内部也在并行)

  • 流水线:多条指令不同阶段重叠执行。
  • 超标量 + 乱序执行:一个周期发射多条指令。
  • SIMD(AVX 等):一条指令处理多个数据。

这些发生在“单个物理核的微架构里”,也是真并行。

4. GPU / 专用加速器
成千上万线程真正同时跑在大量流处理器上,大规模并行。

1.2.3 总结

在理解的时候,不必把上面都记住,只需要简单理解:

一、并发和并行是多个线程在执行时间上的状态描述,

1.并发描述的是多个线程非同时进行,2.并行描述的是多个线程同时进行,

二、而至于具体的怎么进行、分配给谁进行,都是调度的活

2.进程介绍

2.1 进程的资源分配是如何进行的?

操作系统通过维护一个数据结构——PCB(Process Control Block 进程控制块),来完成对进程的资源分配与管理。这 7 个要点构成了分配与管理的核心:

2.1.1 确定资源归属

  1. PID(进程 ID):操作系统分配的唯一标识符。所有分配给该进程的资源,都在系统内核中挂载在这个 PID 名下。
  2. 内存指针:操作系统为该进程划分独立的虚拟地址空间(包含代码段、数据段、堆栈等)。内存指针记录了这些空间在物理内存或虚拟内存中的基址。这块内存空间是该进程独占的。
  3. 文件描述符表:操作系统将 I/O 设备(硬盘文件、网卡 Socket 等)抽象为“文件”。分配给该进程的外部资源,以文件描述符(File Descriptor)的形式记录在此表中,进程通过索引该表来操作外部设备。

2.1.2 确定资源调度(重点)

CPU 是最核心的计算资源。操作系统通过 PCB 中的以下字段,决定何时将 CPU 分配给该进程:

  1. 进程状态:记录进程当前是就绪(等待 CPU)、运行(占用 CPU)还是阻塞(等待 I/O)。
  2. 进程优先级:决定进程获取 CPU 时间片的先后顺序。
  3. 进程的上下文:当进程被剥夺 CPU 时,保存当前 CPU 寄存器的值(如程序计数器 PC、栈指针等),以便下次获得 CPU 时恢复现场继续执行。
  4. 记账信息:记录进程已消耗的 CPU 时间、内存使用量等,用于调度算法和资源限制。

2.1.3 进程的弊端

一、虚拟内存?页表?TLB?
1.虚拟内存的创作背景:为什么要搞出这个“假象”?

早期的计算机直接使用物理地址。这带来三个致命问题:程序稍大点内存装不下、A 程序写错指针容易踩踏 B 程序的数据、内存碎片多。

为了解决这些问题,操作系统引入了虚拟内存(Virtual Memory)。

2. 虚拟内存的概念:数据到底存在哪?

操作系统给每个进程画了一个“大饼”:让每个进程都以为自己独占了一整块连续、巨大的内存(从 0 开始)。但实际上:
1)数据首选真实的物理内存:程序真正活跃的数据,绝大多数都存放在真实的物理内存条里。
2.)硬盘作为备用仓库:物理内存空间(就是内存空间,这样叫是为了区分虚拟内存)是有限的。当物理内存快满时,操作系统会把一部分暂时不用的数据,悄悄挪到硬盘上的 Swap(交换分区)里。
3.)动态搬运:当进程需要用到那块被丢到硬盘的数据时,操作系统再把它换回物理内存。

3.TLB 的登场:虚拟内存带来的“性能代价”与硬件破局

虚拟内存很完美,但带来了巨大的性能开销:因为进程用的是“虚拟地址”,它并不知道真实数据在物理内存的哪里,操作系统必须维护一张页表(Page Table)来做翻译。

如果没有 TLB,CPU 找数据会经历极其痛苦的“两次访问内存”:
1. CPU 拿到虚拟地址,必须先跑去 内存条 里查“页表”,翻译出真实的物理地址(第一次访问内存,极慢)。

2. 拿到物理地址后,CPU 再顺着地址跑去 内存条 里拿真正的数据(第二次访问内存,极慢)。
内存条的速度比起 CPU 简直像蜗牛,每次查表都要浪费大量时间,系统会被严重拖慢。


为了解决这个速度问题,CPU 硬件里加了一个极速缓存——TLB(Translation Lookaside Buffer,转译后备缓冲区)。

*TLB 实际上就是“页表”最常用内容的“随身小抄”。CPU 会把最近查过的那几条页表记录,直接抄在自己的硬件里。下次 CPU 拿到虚拟地址,直接在 CPU 内部查 TLB(小抄)。如果命中(找到了),立刻就知道物理地址,根本不用去内存里查那本笨重的大页表了。

二、进程级别操作的巨大开销
  1. 创建/销毁开销大:创建一个新进程,操作系统必须为其分配独立的物理内存、建立全新的 PCB、复制页表和文件描述符等。销毁时则需要回收所有这些资源。
  2. 上下文切换开销大:在多个进程间切换 CPU 时,由于进程拥有独立的内存地址空间,CPU 不仅需要切换寄存器,还需要切换页表(导致 TLB 失效,缓存命中率下降),开销极大。
  3. 通信成本高:进程间内存隔离,交换数据必须依赖操作系统提供的 IPC(进程间通信)机制(如管道、消息队列),效率低。


为了避免上述开销,操作系统在进程内部引入了线程(Thread)。

  1. 资源共享:线程不拥有独立的内存和文件资源。同一个进程内的多个线程 共享 该进程的 PCB 中记录的内存指针(地址空间)和文件描述符表。线程直接在进程已分配好的资源上进行任务执行。
  2. 轻量级操作:创建或销毁一个线程,只需分配或回收极少量的资源(如独立的栈空间和 TCB 线程控制块),无需重新分配整个进程的资源。同一个进程内的线程切换,不需要切换内存地址空间,极大降低了上下文切换的开销。

线程部分在下一章进行介绍。

Logo

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

更多推荐