1. 进程、线程与协程的本质差异
在操作系统和并发编程领域,进程、线程和协程这三个概念构成了现代计算的基础架构。它们之间的关系就像建筑工地上的不同工作模式:进程相当于独立的施工团队,各自有全套工具和材料;线程是团队内的工人,共享团队资源但各自执行不同任务;协程则像是工人之间的灵活协作,不需要外部调度就能自主协调工作。
1.1 进程:系统资源的独立王国
进程是操作系统进行资源分配的最小单位,每个进程都拥有:
- 独立的虚拟地址空间(32位系统通常是4GB)
- 单独的文件描述符表
- 独立的内存管理单元(MMU上下文)
- 安全隔离的用户权限和进程凭证
重要提示:进程间通信(IPC)必须通过显式机制,如管道、消息队列或共享内存。Linux下的
fork()系统调用创建的子进程会获得父进程所有资源的完整拷贝(写时复制机制)。
现代操作系统通过进程描述符(Linux中的task_struct)管理进程,这个数据结构包含:
c复制// 简化的进程控制块示意
struct task_struct {
pid_t pid; // 进程ID
volatile long state; // 运行状态
struct mm_struct *mm; // 内存管理信息
struct files_struct *files; // 打开文件表
// ... 其他字段约100+项
};
进程切换的成本主要来自:
- TLB(转换后备缓冲器)刷新
- 寄存器上下文保存/恢复
- 调度器决策开销
- 缓存局部性失效
实测数据显示,现代Linux系统上进程切换通常需要1-10微秒,创建新进程则需要1-10毫秒。
1.2 线程:轻量化的执行单元
线程解决了进程的"重量级"问题,同一进程内的多个线程共享:
- 相同的虚拟地址空间
- 打开的文件描述符
- 信号处理程序
- 用户ID和组ID
但每个线程独立拥有:
- 线程ID(TID)
- 寄存器集合(包括程序计数器)
- 栈空间(通常8MB左右)
- 错误号errno
- 信号掩码
Linux通过clone()系统调用实现线程,关键参数:
bash复制clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, ...);
这些标志位决定了资源共享的程度。
线程切换开销约为进程的1/10,主要因为:
- 无需切换地址空间
- TLB无需刷新
- 缓存命中率更高
但线程的共享特性带来了同步问题,典型的竞态条件:
python复制# 多线程共享变量的危险示例
counter = 0
def increment():
glo
