【系统编程】线程简介
文章目录
- 一、进程与线程的关系
- 二、线程内核实现原理
- 2.1 虚拟地址三级映射过程
- 2.2 线程共享资源
- 2.3 线程的非共享资源
- 2.4 线程的状态
- 三、线程优缺点
一、进程与线程的关系
1. 进程(Process)
- 是程序在执行中的一个实例。
- 拥有独立的地址空间(代码段、数据段、堆、栈等)。
- 是操作系统分配资源的最小单位。
- 每个进程由操作系统管理,并配有一个进程控制块(PCB),用于保存进程状态信息。
- 运行时常驻内存,独立于其他进程。
2. 线程(Thread)
- 是进程中的一个执行实体,是真正“干活”的单位。
- 是 CPU 调度和执行的最小单位。
- 通常也称为轻量级进程(Light Weight Process, LWP)。
- 一个进程可以包含一个或多个线程(多线程),至少有一个主线程。
- 线程共享所属进程的资源(如代码段、堆、文件描述符等),但每个线程有自己的栈、程序计数器和寄存器。
3. 比较总结
比较维度 | 进程 | 线程 |
---|---|---|
是否独立 | 是,拥有独立的地址空间 | 否,依附于进程 |
资源分配 | 是系统分配资源的最小单位 | 不分配资源,仅使用进程资源 |
调度单位 | 不是,进程中线程才被调度 | 是系统调度和执行的最小单位 |
开销大小 | 创建、销毁、切换开销较大 | 相对较小,效率高 |
共享性 | 不共享地址空间 | 共享进程地址空间与资源 |
通信方式 | 使用 IPC 机制,开销大 | 直接读写共享内存,开销小 |
- Linux 系统中,线程 LWP 称为轻量级进程。
- 进程:有独立的进程地址空间,有独立的 PCB,最小的资源分配单位。
- 线程:有独立的 PCB,没有独立的进程地址空间(与其他线程共享),最小执行单位
- 一个创建了线程的进程,本身也沦落为线程。
LWP号
:CPU 划分时间片的最小依据(最小执行单位)- 查看
LWP号
:ps -Lf 进程pid
- 查看
二、线程内核实现原理
2.1 虚拟地址三级映射过程
对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。
但!线程不同!两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空间。
PCB --> 页目录(Page Directory)--> 页表(Page Table)--> 物理页框 --> 内存单元
- 页表:可看成数组,首地址位于PCB中
- 不同进程的同一虚拟地址,实际指向不同的物理页面 ➜ 因为有不同的页表。
- 线程则不同,多个线程共享同一套页表,因此它们看到的虚拟地址空间是完全一致的。
在 Linux 内核中不区分“线程”与“进程”,它们都是“任务”(task),由
task_struct
结构统一管理。
- 本质上,Linux 创建进程和线程都是通过底层
clone()
系统调用实现的。 fork()
和pthread_create()
虽然是用户接口不同,但都依赖clone()
。clone()
提供了一系列参数,决定子任务与父任务之间共享哪些资源。
2.2 线程共享资源
在同一个进程中的多个线程,共享以下资源,这也是线程通信效率高的根本原因:
-
文件描述符表
- 所有线程共享打开的文件、socket 等资源。
-
每种信号的处理方式
- 比如对
SIGINT
的处理函数,是线程共享的。
- 比如对
-
当前工作目录
getcwd()
和chdir()
在所有线程中体现一致。
-
用户 ID 和组 ID
- 所有线程具有相同的用户身份(UID/GID)。
-
内存地址空间(包括):
.text
段(代码段).data
段(已初始化全局变量).bss
段(未初始化全局变量)- heap堆区(malloc 分配内存)
- 共享库
2.3 线程的非共享资源
线程拥有自己独立的运行环境信息,每个线程在同一进程中也有自己独立的数据,用于保持各自的执行状态:
-
线程 ID(TID)
- 每个线程有唯一 ID(在 Linux 下是一个 PID)。
-
处理器上下文(寄存器)与栈指针(包括内核栈)
- 线程切换时保存的执行现场信息。
-
栈空间(用户栈、内核栈)
- 每个线程都有自己的栈空间,用于函数调用和局部变量存储。
-
errno
变量- 每个线程维护独立的
errno
,以避免冲突。
- 每个线程维护独立的
-
信号屏蔽字(signal mask)
- 控制本线程屏蔽哪些信号,与其他线程可不同。
-
调度优先级
- 可为不同线程设置不同调度策略和优先级。
2.4 线程的状态
1. 准备状态(Ready)
- 线程已被创建或刚从阻塞状态解除。
- 各种条件都满足,只是还没被 CPU 调度执行。
- 由
pthread_create()
或被唤醒进入此状态。
2. 运行状态(Running)
- 获得 CPU 时间片后进入运行状态。
- 多核 CPU 上可能多个线程同时运行。
- 如果不加同步机制(如锁、信号量等),可能造成竞态条件或死锁。
3. 阻塞状态(Blocked)
- 正在等待某些外部事件或条件满足。
- 常见阻塞原因包括:
- 等待 I/O 响应(磁盘/网络等)。
- 等待互斥锁(mutex)释放。
- 等待条件变量(
pthread_cond_wait
)。 - 等待信号(如
sigwait()
)。
4. 终止状态(Terminated)
- 线程执行完返回、调用
pthread_exit()
,或被取消(pthread_cancel()
)。 - 此时线程的资源会释放或等待被
pthread_join()
回收。
三、线程优缺点
优点:
- 提高程序并发性
- 开销小
- 数据通信、共享数据方便
缺点:
- 库函数,不稳定
- 调试、编写困难、gdb不支持
- 对信号支持不好
Linux下由于实现方法导致进程、线程差别不是很大。