hello~ 很高兴见到大家! 这次带来的是Linux系统中关于线程这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙

文章目录
一、线程ID及进程地址空间布局

- 函数 pthread_create 调用之后会返回一个POSIX 线程 ID(pthread_t),这个线程 ID 跟 LWP(轻量级进程)不是同一个东西。LWP 是 Linux 内核底层用于唯一标识轻量级线程的内核线程 ID(TID),而 pthread_create 返回的线程 ID 本质上是一个用户态虚拟地址,它指向一个 glibc 库内部的 struct pthread 线程结构体,这个结构体内部封装了LWP、线程属性、栈信息、分离 / 可连接状态、返回值、同步变量等线程运行所需的全部上下文信息。
- 图中 struct pthread 线程结构体,是由 glibc 在 pthread_create 内部通过 mmap 系统调用分配的匿名私有内存块,位于进程地址空间的mmap 区域(mmap区域和共享区挨在一起,一个私有,一个共享)。这块内存会一次性分配,包含 struct pthread、线程局部存储、线程栈等完整线程资源,仅属于当前进程的当前线程,不与其他进程共享。
- 函数 pthread_create 内部会调用 clone 系统调用来完成线程的创建,因此 pthread 系列函数本质上就是对 Linux 轻量级进程相关系统调用的封装。
- 我们用 pthread_create 函数传递参数给线程要执行的函数时,参数是怎么传入的呢?系统会把参数保存到该线程对应的 pthread 结构体内部,线程执行时再从中取出使用。我们能够通过 pthread_join 获取到线程的返回值,也正是依靠 pthread 结构体来存储返回值。
- 主线程拥有自己独立的栈区,这个栈区仅供主线程使用,并不在 mmap 区域。而通过 pthread_create 创建的子线程,每个都有自己专属的线程栈,这些线程栈统一位于 mmap 匿名区域中。

- 我们也可以梳理一下为什么线程需要被等待:线程结束后,LWP 就消亡了,而pthread 结构体与内核管理轻量级进程的 task_struct 是 1:1 的关系,线程函数的返回值会由 glibc 保存到 pthread 结构体中,而我们要获取这些信息,就必须通过 pthread_join 从结构体里读取,并由库函数释放这块通过 mmap 申请的内存空间。因此,如果不等待、不回收线程,就会造成内存泄漏。
- 再来讲一下 pthread_create 函数的第二个参数 ------ 线程属性,线程的各类属性被封装在 pthread_attr_t 结构体中。pthread_attr_t 线程属性结构体是 struct pthread 的内部成员,它会随线程结构体一同创建、一同存储在 mmap 匿名区、一同被释放。日常编程中一般不需要自定义属性,给第二个参数传递 NULL 时,库函数会自动使用默认属性初始化线程结构体内部的属性成员。
cpp
// 1. 线程属性结构体 (你问的 pthread_attr_t)
typedef struct {
int detachstate; // 分离状态:可结合 / 分离
size_t stacksize; // 线程栈大小
void *stackaddr; // 栈地址
int sched_policy; // 调度策略
} pthread_attr_t;
// 2. 核心线程结构体 struct pthread
struct pthread {
// 内核LWP标识 (1:1对应task_struct)
pid_t tid; // LWP号,系统调用gettid获取
pid_t pid; // 所属进程PID
// 线程属性结构体 直接嵌套在内部
pthread_attr_t attr;
// 线程运行核心信息
void *(*start_routine)(void *); // 线程入口函数
void *arg; // 线程参数
void *retval; // 线程返回值(join获取)
// 内存信息(mmap匿名区分配)
void *stack_base; // 线程栈基地址
size_t stack_size; // 线程栈总大小
// 同步控制(用于pthread_join等待)
pthread_mutex_t join_mutex;
pthread_cond_t join_cond;
};
// pthread_t 本质是指向该结构体的指针
typedef struct pthread *pthread_t;
- C++11 及后续标准提供了官方的线程库,该库底层封装了不同操作系统的原生线程实现(Linux 下封装 pthread,Windows 下封装系统原生线程接口),对外暴露统一的跨平台接口,让一份线程代码可以在 Windows、Linux 等不同系统上直接运行,实现跨平台特性。
今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!