Linux 线程概念
线程的基本概念
线程是在一个程序里的一个执行路线,更准确的定义是:线程是”一个进程内部的控制序列”。一切进程至少都有一个执行线程,线程在进程内部运行,本质是在进程地址空间内运行。
在Linux系统中,从CPU的角度来看,线程对应的PCB(进程控制块)比传统的进程更加轻量化。通过进程的虚拟地址空间,我们可以看到进程的大部分资源,将这些资源合理分配给每个执行流,就形成了线程执行流。

线程与进程的关系
我们可以用一个家庭成员来形象地理解线程、执行流和进程之间的关系:
- 进程就像一个家庭,拥有共同的资源(房子、存款、家具等)
- 线程就像家庭中的各个成员(父母、孩子),共享家庭资源但各自有自己的任务
- 执行流就像家庭成员各自的活动轨迹(爸爸上班、妈妈做饭、孩子学习)
资源分配与执行
- 进程是承担分配系统资源的基本实体,它拥有独立的地址空间、文件描述符、信号处理等资源
- 线程是进程内部的执行流单元,多个线程共享进程的资源,但每个线程有自己的执行上下文
线程的实现机制
分页式存储管理
现代操作系统使用虚拟内存和分页机制来管理内存:
- 将物理内存按照固定长度的页框(通常4KB)进行分割
- 为每个进程提供独立的虚拟地址空间(32位系统为0~4GB)
- 通过页表建立虚拟地址和物理地址的映射关系
这种机制允许:
- 连续的虚拟内存映射到不连续的物理内存页
- 解决物理内存碎片问题
- 实现内存保护和共享
多级页表结构
为了高效管理页表,现代CPU使用多级页表结构:
- 页目录表:由CR3寄存器指向,包含1024个页表项
- 页表:每个页表包含1024个页表项,指向物理页
- TLB快表:缓存最近使用的页表项,加速地址转换
当CPU访问内存时,MMU(内存管理单元)会:
- 首先查询TLB快表
- 如果未命中,则查询多级页表
- 如果页表项不存在,则触发缺页异常
线程的优势
- 创建代价低:创建线程比创建进程开销小得多
- 切换速度快:
- 线程切换不改变虚拟内存空间,TLB不需要刷新
- 只需要保存/恢复少量寄存器内容
- 资源占用少:线程共享进程资源,额外开销小
- 并行性好:能充分利用多处理器核心
- I/O重叠:可以在等待I/O时执行其他计算任务
线程的缺点
- 性能损失:计算密集型线程过多可能导致额外调度开销
- 健壮性降低:一个线程崩溃可能导致整个进程终止
- 编程复杂:需要考虑线程同步和数据竞争问题
- 缺乏隔离:线程间缺乏保护机制,容易相互干扰
线程与进程的资源对比
线程共享的资源
- 代码段(Text Segment)
- 数据段(Data Segment)
- 堆空间
- 文件描述符表
- 信号处理方式
- 用户ID和组ID
- 当前工作目录
线程独有的资源
- 线程ID
- 寄存器组(线程上下文)
- 栈空间
- errno变量
- 信号屏蔽字
- 调度优先级
线程虽然是进程内的执行分支,共享进程的代码、数据、堆等资源,但每个线程仍然需要维护自己的独立执行环境。其中,寄存器组(线程上下文)和栈空间是线程独有资源的关键部分,它们确保了线程可以独立执行,不受其他线程干扰。
1. 寄存器组(线程上下文)
(1)为什么线程需要独立的寄存器组?
CPU 执行指令时,依赖寄存器存储当前的计算状态,例如:
- 程序计数器(PC / EIP / RIP):记录当前执行指令的地址
- 栈指针(SP / ESP / RSP):指向当前线程的栈顶
- 通用寄存器(EAX, EBX, ECX, EDX…):存储临时计算结果
- 状态寄存器(EFLAGS):记录运算状态(如进位、溢出等)
由于 线程是 CPU 调度的基本单位,操作系统在切换线程时,必须保存当前线程的寄存器状态(上下文),并恢复下一个线程的寄存器状态,否则计算会出错。
(2)线程切换时寄存器的保存与恢复
线程切换(上下文切换)的过程:
- 保存当前线程的寄存器状态(存入线程的
task_struct或TCB结构体)。 - 加载目标线程的寄存器状态(从它的
task_struct恢复)。 - CPU 继续执行目标线程。
如果多个线程共享同一套寄存器,切换时数据会混乱,因此 每个线程必须有自己的寄存器副本。
2. 栈空间
(1)为什么线程需要独立的栈?
栈(Stack)用于存储:
- 函数调用时的返回地址(调用
call指令时压栈) - 局部变量
- 函数参数
- 临时数据
如果多个线程共享同一个栈:
- 函数调用链会混乱(A 线程调用
func1(),B 线程调用func2(),栈帧会交错,导致程序崩溃)。 - 局部变量可能被覆盖(线程 A 的变量可能被线程 B 修改)。
因此,每个线程必须有自己的栈空间,确保函数调用和局部变量的独立性。
(2)线程栈的分配方式
- 主线程:通常使用进程的默认栈(由操作系统或编译器分配)。
- 子线程:由
pthread_create()动态分配栈(默认大小通常为 2~8MB,可调整)。
线程作为轻量级的执行单元,在现代操作系统中扮演着重要角色。理解线程的工作原理、资源分配方式以及与进程的关系,对于编写高效、可靠的并发程序至关重要。合理使用多线程可以显著提升程>序性能,但也需要注意线程安全、同步和资源管理等问题。