Linux 多线程初探:从进程到轻量级执行流
线程的本质与定义
线程是进程内的一个执行分支,其执行粒度比进程更细。在操作系统视角下,线程是调度的基本单位,而进程则是资源分配的基本实体。
以前我们常认为进程只有一个执行流,这其实是一种特殊情况。在现代系统中,一个进程内部包含多个执行流才是常态。内核中,每个执行流对应一个 task_struct 结构体,但所有线程共享同一个地址空间。这意味着任何执行流要运行,都必须依赖进程拥有的资源窗口(如动态库加载、内存申请等)。
进程与线程的资源关系
虚拟地址空间的映射
理解线程的关键在于理解地址空间。CPU 中的 CR3 寄存器存储页表目录地址,负责将虚拟地址转换为物理地址。以 32 位系统为例,虚拟地址被分为三部分(10+10+12),前 20 位定位页框起始位置,后 12 位作为偏移量。
创建进程是一个重操作,因为需要建立完整的地址空间映射。而线程本质上是在已有的地址空间内划分范围进行资源分配,因此开销更小。
Linux 下的线程实现
Linux 并没有独立于进程之外的线程数据结构。它复用进程的内核数据结构 task_struct 来模拟线程,这种机制被称为'轻量级进程'(LWP)。
注意:CPU 无法直接区分 Linux 中的进程和线程,对 CPU 而言它们都是执行流。内核通过调度算法管理这些轻量级进程。
线程的优缺点分析
优势
- 轻量级:创建和释放线程的代价远小于进程,生命周期更短。
- 切换高效:线程间切换只需保存少量上下文(如寄存器、栈指针),无需切换页表。
- 资源共享:线程共享进程的数据段、代码段等资源,通信方便。
- 并行能力:能充分利用多处理器的并行计算能力,适合计算密集型任务。
- I/O 重叠:在等待慢速 I/O 时,其他线程可继续执行计算,提升用户体验。
劣势
- 性能损耗:若计算密集型线程数量超过处理器核心数,过多的同步和调度开销会导致性能下降。
- 健壮性降低:线程间缺乏保护,一个线程的错误(如野指针、除零)可能导致整个进程崩溃。
- 访问控制弱:进程是访问控制的基本粒度,线程调用某些 OS 函数可能影响整个进程。
- 编程复杂:调试多线程程序比单线程困难得多,需处理竞态条件、死锁等问题。
Linux 线程接口实战
虽然内核层面只有轻量级进程的概念,但应用层提供了标准的 POSIX 线程库 pthread 来封装接口。
编译与链接
几乎所有 Linux 平台都默认自带该库。编写多线程代码时,需显式链接 pthread 库:
g++ -o $@ $^ -lpthread
线程创建 (pthread_create)
使用 pthread_create 创建新线程,其参数含义如下:
- 第一个参数:输出型参数,用于接收新线程的 ID。
- 第二个参数:线程属性,通常设为
nullptr使用默认属性。 - 第三个参数:函数指针,指向线程执行的入口函数(返回
void*,接受void*参数)。 - 第四个参数:传递给线程函数的实参。
成功返回 0,失败则返回错误码(不会设置全局 errno)。需要注意的是, 不是系统调用,而是动态库函数。


