【Linux】线程概念与控制(3)_地址空间布局

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


文章目录


一、线程ID及进程地址空间布局

  1. 函数 pthread_create 调用之后会返回一个POSIX 线程 ID(pthread_t),这个线程 ID 跟 LWP(轻量级进程)不是同一个东西。LWP 是 Linux 内核底层用于唯一标识轻量级线程的内核线程 ID(TID),而 pthread_create 返回的线程 ID 本质上是一个用户态虚拟地址,它指向一个 glibc 库内部的 struct pthread 线程结构体,这个结构体内部封装了LWP、线程属性、栈信息、分离 / 可连接状态、返回值、同步变量等线程运行所需的全部上下文信息。
  2. 图中 struct pthread 线程结构体,是由 glibc 在 pthread_create 内部通过 mmap 系统调用分配的匿名私有内存块,位于进程地址空间的mmap 区域(mmap区域和共享区挨在一起,一个私有,一个共享)。这块内存会一次性分配,包含 struct pthread、线程局部存储、线程栈等完整线程资源,仅属于当前进程的当前线程,不与其他进程共享
  3. 函数 pthread_create 内部会调用 clone 系统调用来完成线程的创建,因此 pthread 系列函数本质上就是对 Linux 轻量级进程相关系统调用的封装。
  4. 我们用 pthread_create 函数传递参数给线程要执行的函数时,参数是怎么传入的呢?系统会把参数保存到该线程对应的 pthread 结构体内部,线程执行时再从中取出使用。我们能够通过 pthread_join 获取到线程的返回值,也正是依靠 pthread 结构体来存储返回值
  5. 主线程拥有自己独立的栈区,这个栈区仅供主线程使用,并不在 mmap 区域。而通过 pthread_create 创建的子线程,每个都有自己专属的线程栈,这些线程栈统一位于 mmap 匿名区域中。
  1. 我们也可以梳理一下为什么线程需要被等待:线程结束后,LWP 就消亡了,而pthread 结构体与内核管理轻量级进程的 task_struct 是 1:1 的关系,线程函数的返回值会由 glibc 保存到 pthread 结构体中,而我们要获取这些信息,就必须通过 pthread_join 从结构体里读取,并由库函数释放这块通过 mmap 申请的内存空间。因此,如果不等待、不回收线程,就会造成内存泄漏。
  2. 再来讲一下 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;
  1. C++11 及后续标准提供了官方的线程库,该库底层封装了不同操作系统的原生线程实现(Linux 下封装 pthread,Windows 下封装系统原生线程接口),对外暴露统一的跨平台接口,让一份线程代码可以在 Windows、Linux 等不同系统上直接运行,实现跨平台特性。

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

相关推荐
keyipatience14 分钟前
25.Linux静态动态库全解析
linux·运维·服务器
爱睡觉11128 分钟前
在 Android 模拟器 Shell 下运行 ncnn 推理的性能排查记录
linux·shell
落羽的落羽1 小时前
【项目】JsonRpc框架——开发实现1(细节功能、字段定义、抽象层、具象层)
linux·服务器·网络·c++·人工智能·算法·机器学习
shixuzhimeng1 小时前
FTP服务器项目
linux·网络·ftp
Chris-zz1 小时前
Linux:线程概念与控制
linux·运维
剑神一笑1 小时前
Linux chown 命令详解:从 inode 到实战
linux·运维·服务器
MIXLLRED2 小时前
随笔——在 Ubuntu 22.04 中查看 Markdown (.md) 文件
linux·运维·ubuntu·markdown
STDD2 小时前
Linux cgroup v2 资源控制实战:限制进程 CPU/内存/IO,systemd slice 管理
linux·运维·服务器
kukubuzai3 小时前
Docker Note
linux·运维·docker
Ltd Pikashu3 小时前
insmod 加载内核模块 —— sys_init_module 源码剖析
linux·kernel·insmod