
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- [1 ~> Linux 线程概念](#1 ~> Linux 线程概念)
-
- [1.1 线程的深度定义与内核实现](#1.1 线程的深度定义与内核实现)
- [1.2 内存管理的底层:分页式存储](#1.2 内存管理的底层:分页式存储)
-
- [1.2.1 物理内存的页框管理](#1.2.1 物理内存的页框管理)
- [1.2.2 虚拟地址到物理地址的 10-10-12 转换](#1.2.2 虚拟地址到物理地址的 10-10-12 转换)
- [1.2.3 补充:MMU 的权限过滤机制](#1.2.3 补充:MMU 的权限过滤机制)
- [1.3 线程切换的硬件优势](#1.3 线程切换的硬件优势)
- [2 ~> 进程与线程的资源边界](#2 ~> 进程与线程的资源边界)
-
- [2.1 线程私有资源(不可共享)](#2.1 线程私有资源(不可共享))
- [2.2 线程共享资源](#2.2 线程共享资源)
- [3 ~> 线程控制实战(Pthread 库)](#3 ~> 线程控制实战(Pthread 库))
-
- [3.1 线程创建:pthread_create](#3.1 线程创建:pthread_create)
- [3.2 线程终止](#3.2 线程终止)
-
- [3.2.1 线程终止的三种方式](#3.2.1 线程终止的三种方式)
- [3.2.2 注意](#3.2.2 注意)
- [3.3 线程等待与资源回收](#3.3 线程等待与资源回收)
- [3.4 补充](#3.4 补充)
-
- [3.4.1 补充:PTHREAD_CANCELED 的本质](#3.4.1 补充:PTHREAD_CANCELED 的本质)
- [3.4.2 线程退出的内存陷阱](#3.4.2 线程退出的内存陷阱)
- [4 ~> 线程 ID 的本质与内存布局(线程库与内核的映射关系)](#4 ~> 线程 ID 的本质与内存布局(线程库与内核的映射关系))
-
- [4.1 库级别的线程标识 pthread_t](#4.1 库级别的线程标识 pthread_t)
- [4.2 LWP 与 pthread_t 的对应关系](#4.2 LWP 与 pthread_t 的对应关系)
- [4.3 LWP 与 pthread_t](#4.3 LWP 与 pthread_t)
- [4.4 线程局部存储(TLS)](#4.4 线程局部存储(TLS))
- [5 ~> 线程封装的工程实践](#5 ~> 线程封装的工程实践)
-
- [5.1 面向对象封装思路](#5.1 面向对象封装思路)
- [5.2 封装代码片段](#5.2 封装代码片段)
- [6 ~> 核心补充:clone 系统调用](#6 ~> 核心补充:clone 系统调用)
-
- [对比:进程 vs 线程资源共享表](#对比:进程 vs 线程资源共享表)
- 补充:线程与进程对信号的处理差异
- 结尾
1 ~> Linux 线程概念
1.1 线程的深度定义与内核实现
(1) 在 Linux 内核中,线程被称为轻量级进程(LWP, Light Weight Process)。
(2) 传统的进程模型中,一个进程对应一个地址空间,对应一个 task_struct;但在 Linux 线程模型中,多个 task_struct 指向同一个 mm_struct(进程地址空间)。
(3) 线程是"一个进程内部的控制序列",它在进程的虚拟地址空间内运行。
(4) CPU 在进行调度时,并不区分进程和线程,它只认 task_struct。因为线程的 task_struct 共享了大量的进程资源,所以其创建、切换、销毁的开销远小于传统意义上的进程。
(5) 进程是承担分配系统资源的基本实体,而线程是 CPU 调度的基本单位。
1.2 内存管理的底层:分页式存储
1.2.1 物理内存的页框管理
(1) 操作系统将物理内存划分为固定大小的块,称为页框(Page Frame),通常大小为 4KB。
(2) 内核使用 struct page 数组来管理所有物理页。这是一个联合体结构,用于减少内存占用。
(3) _mapcount 成员记录该物理页被多少个页表项映射,是内存回收的关键引用计数。
(4) flags 描述页的状态,如是否为脏页(Dirty)、是否被锁定(Locked)。
1.2.2 虚拟地址到物理地址的 10-10-12 转换
(1) 在 32 位环境下,虚拟地址被逻辑上划分为三部分,用于多级页表索引。
(2) 页目录索引(高 10 位):从 CR3 寄存器获取页目录基地址,通过这 10 位找到对应的页目录项(PDE)。
(3) 页表索引(中间 10 位):PDE 指向一个具体的页表,通过这 10 位找到页表项(PTE)。
(4) 页内偏移(低 12 位):PTE 存储物理页框的起始地址,加上这 12 位偏移量(2^12 = 4KB),精确定位物理内存字节。
1.2.3 补充:MMU 的权限过滤机制

1.3 线程切换的硬件优势
(1) 进程切换开销大:需要切换页表、刷新 TLB、刷新处理器的 L1/L2/L3 Cache。
(2) 线程切换开销小:由于共享同一个页表,TLB 缓存的大部分转换映射依然有效。
(3) 缓存热度:线程切换时,CPU 的 Cache 中缓存的数据和指令依然具有高度的重合性,这使得线程在切换后能迅速进入高频执行状态,不会产生进程切换时的"性能塌陷"。
2 ~> 进程与线程的资源边界
2.1 线程私有资源(不可共享)
(1) 线程 ID:在进程内部唯一标识该执行流。
(2) 寄存器组:包含 PC 指针、栈指针及通用寄存器,保存线程当前的执行上下文。
(3) 独立栈空间:每个线程必须拥有独立的函数调用栈,以维护各自的局部变量和调用关系。
(4) errno:每个线程拥有独立的错误码副本,防止多线程环境下错误信息被覆盖。
(5) 信号屏蔽字:各线程可以对不同的信号进行屏蔽。
2.2 线程共享资源
(1) 代码段与数据段:所有线程共享全局变量、静态变量。
(2) 文件描述符表:一个线程打开文件,其他线程可以直接使用该 FD。
(3) 信号处理方式:若某一线程修改了 SIGINT 的 handler,整个进程的行为都会改变。
3 ~> 线程控制实战(Pthread 库)
3.1 线程创建:pthread_create
(1) 线程库是一个用户态库(NPTL),必须通过 -lpthread 进行手动链接。
(2) pthread_create 的第四个参数 void *arg 是万能指针,可以传递基本类型,也可以传递结构体/类对象地址。
cpp
#include <iostream>
#include <pthread.h>
#include <unistd.h>
void* thread_routine(void* args) {
char* msg = (char*)args;
while (true) {
std::cout << "Sub thread: " << msg << " PID: " << getpid() << std::endl;
sleep(1);
}
}
int main() {
pthread_t tid;
pthread_create(&tid, nullptr, thread_routine, (void*)"Hello Thread");
while (true) {
std::cout << "Main thread running..." << std::endl;
sleep(1);
}
return 0;
}
3.2 线程终止
3.2.1 线程终止的三种方式
(1)线程函数 return。
(2)调用 pthread_exit(void *value_ptr),注意 value_ptr 不能指向栈上的局部变量。
(3)调用 pthread_cancel(pthread_t thread) 取消目标线程。
3.2.2 注意
在任何线程中调用 exit 都会导致整个进程退出。
3.3 线程等待与资源回收
(1) pthread_join:主线程必须调用此函数回收子线程资源,否则会产生类似"僵尸进程"的残留问题。
(2) 返回值获取 :通过 void **retval 参数,可以获取线程函数 return 的结果或 pthread_exit 传出的数据。
(3) pthread_cancel :用于取消正在运行的线程。被取消的线程其退出码固定为宏 PTHREAD_CANCELED(即 -1)。

3.4 补充
3.4.1 补充:PTHREAD_CANCELED 的本质
当一个线程是被 pthread_cancel 异常取消退出的,通过 pthread_join 获取到的退出码是一个宏定义 PTHREAD_CANCELED,在大多数系统中,其数值为 -1(即 (void*)-1)。
3.4.2 线程退出的内存陷阱
在使用 pthread_exit 或 return 返回数据给主线程时,严禁返回局部变量的地址 。由于子线程栈在退出后会被销毁或重用,主线程拿到的指针将指向野内存。建议使用全局变量、静态变量或 malloc 申请的堆空间。
4 ~> 线程 ID 的本质与内存布局(线程库与内核的映射关系)
4.1 库级别的线程标识 pthread_t
(1) 线程库通过 mmap 在进程地址空间的共享区为每个线程分配了一块内存,这块内存被称为线程控制块(TCB)。
(2) pthread_t 的数值本质上就是这块 TCB 在共享区内的起始地址。
(3) 子线程的栈也是在这块 mmap 出来的空间内。
4.2 LWP 与 pthread_t 的对应关系
(1) LWP 是内核标识,用于 OS 调度,全局唯一。
(2) pthread_t 是库标识,仅在进程内部有意义。
(3) 程序员在代码中使用 pthread_t 进行管理,而内核通过映射关系找到对应的 LWP 进行物理调度。
4.3 LWP 与 pthread_t
(1) LWP(Light Weight Process):内核层面的线程 ID,由系统分配。在终端使用 ps -aL 可以查看。
(2) pthread_t:用户态线程库(NPTL)层面的 ID。
(3) 映射关系:NPTL 库在 mmap 区域(共享区)为每个线程开辟了一块空间。pthread_t 的值实际上就是这块内存空间的起始地址。
4.4 线程局部存储(TLS)
(1) 线程库在共享区中为每个线程维护了线程描述符(TCB)、局部变量和独立的栈。
(2) 在 C/C++ 代码中,使用 __thread 关键字修饰全局变量,可以使每个线程拥有一份该变量的私有副本,互不干扰。
5 ~> 线程封装的工程实践
5.1 面向对象封装思路
(1) 在 C++ 中封装线程类,可以将线程的逻辑与资源管理解耦。
(2) 必须处理 this 指针问题:pthread_create 需要一个 void* (*)(void*) 类型的静态函数,而类成员函数隐含了 this 指针作为第一个参数,因此需要将回调函数设为 static,并将类对象的 this 指针作为参数传入。
5.2 封装代码片段
cpp
class Thread {
public:
Thread(std::function<void()> func) : _func(func) {}
static void* start_routine(void* args) {
Thread* t = static_cast<Thread*>(args);
t->_func(); // 执行任务
return nullptr;
}
void run() {
pthread_create(&_tid, nullptr, start_routine, this);
}
void join() {
pthread_join(_tid, nullptr);
}
private:
pthread_t _tid;
std::function<void()> _func;
};
6 ~> 核心补充:clone 系统调用
(1) Linux 下所有的线程创建最终都会调用内核的 clone 系统调用。
(2) 通过传入不同的 flags 掩码(如 CLONE_VM,CLONE_FILES,CLONE_FS),clone 可以灵活控制父子执行流之间资源的共享程度。
(3) 如果不传这些共享标志位,clone 的行为就退化成了 fork。
对比:进程 vs 线程资源共享表

补充:线程与进程对信号的处理差异


结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
【Linux系统:多线程】Linux 内核与多线程深度强化干货25条
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
