一、核心结论
进程是资源分配的基本单位,线程是调度执行的基本单位。二者的核心区别在于 "资源是否共享"------ 进程间完全独立,线程共享进程的虚拟地址空间及大部分资源,仅独占少量执行上下文相关资源
二、资源共享清单(线程共享的进程资源)
同一进程内的所有线程共享以下资源,这是线程通信便捷、创建成本低的核心原因:
- 虚拟地址空间:代码段(Text Segment)、数据段(Data Segment)、堆、共享库、mmap 映射区域
- 文件资源:文件描述符表、信号处理方式(SIG_IGN、SIG_DFL 或自定义处理函数)
- 进程环境:当前工作目录、用户 ID(UID)、组 ID(GID)
- 其他资源:信号量、消息队列、共享内存等进程级通信机制
共享资源的实际意义
- 线程可直接访问全局变量、堆内存,无需跨进程通信;
- 打开的文件可被所有线程复用,无需重复打开;
- 共享库仅加载一次,所有线程共享代码和数据,节省内存
三、资源独占清单(线程私有资源)
为保证独立执行,线程独占以下与执行上下文相关的资源:
- 线程 ID(tid):内核层面的唯一标识,通过
gettid()获取 - 执行上下文:CPU 寄存器(程序计数器 PC、栈指针 SP 等),线程切换时需保存和恢复
- 栈空间:主线程栈在虚拟地址空间的栈区,子线程栈在共享区(通过 mmap 分配),默认大小通常为 8MB,不可动态增长
- 线程局部数据:errno 变量、信号屏蔽字、调度优先级、线程私有存储(TLS);
- 线程属性:栈大小、分离状态、调度策略等
独占资源的实际意义:
- 线程栈私有避免函数调用栈数据冲突
- 信号屏蔽字允许线程独立控制接收哪些信号
- 调度优先级让线程可获得不同的 CPU 时间片
四、进程与线程的核心区别对比表
| 对比维度 | 进程(Process) | 线程(Thread) |
|---|---|---|
| 核心角色 | 资源分配的基本单位 | 调度执行的基本单位 |
| 资源独立性 | 高(独立虚拟地址空间、文件描述符等) | 低(共享进程资源,仅独占执行上下文) |
| 上下文切换成本 | 高(需切换页表、地址空间、寄存器、缓存) | 低(仅切换寄存器和栈,页表和缓存不变) |
| 崩溃影响范围 | 仅自身,不影响其他进程 | 整个进程及所有线程都会崩溃 |
| 通信方式 | 复杂(管道、消息队列、共享内存等) | 简单(直接访问全局变量、堆内存) |
| 创建成本 | 高(需分配虚拟地址空间、页表、文件资源) | 低(仅需初始化执行上下文和私有栈) |
| 内核实现(Linux) | 独立task_struct + 独立mm_struct |
独立task_struct + 共享mm_struct |
五、关键场景验证:进程切换 vs 线程切换
1. 进程切换流程:
- 保存当前进程的
task_struct状态(寄存器、页表指针等) - 切换页表(更新 CR3 寄存器指向新进程的页目录)
- 刷新 TLB(快表),因为页表已改变
- 恢复新进程的
task_struct状态,执行新进程
2. 线程切换流程:
- 保存当前线程的寄存器和栈指针
- 恢复目标线程的寄存器和栈指针
- 无需切换页表和刷新 TLB,因为共享
mm_struct - 执行目标线程
性能差异:
线程切换的成本远低于进程切换,尤其是 TLB 刷新的开销 ------TLB 是虚拟地址到物理地址的缓存,刷新后需重新建立映射,会导致内存访问效率暂时下降
六、新手常见误区
- 误区:线程崩溃只影响自身→纠正:线程共享进程地址空间,一个线程的非法操作(如野指针)会触发信号,导致整个进程终止
- 误区:线程栈和进程栈一样可动态增长→纠正:子线程栈通过 mmap 分配,大小固定,用尽会触发栈溢出
- 误区:进程和线程是完全独立的概念→纠正:Linux 中线程是轻量级进程,二者均用
task_struct描述,仅资源共享方式不同
七、总结
- 进程与线程的核心区别在于 "资源分配" 和 "调度执行" 的职责划分
- 线程共享进程的大部分资源,仅独占执行上下文相关资源,这是其轻量级的本质
- 理解资源共享与独占特性,是后续学习线程同步、通信的基础
下一篇将讲解线程控制的核心操作:线程创建与终止,不见不散啦!!