pthread 库原理

张开发
2026/4/21 17:15:39 15 分钟阅读

分享文章

pthread 库原理
Linux 上没有真正的 “线程”pthread 库 NPTL 把每个线程封装成一个轻量级进程LWP内核只认任务task_struct线程 进程完全平等调度用户态libpthread.soglibc 内置做封装提供 POSIX 线程 API底层架构NPTL现代 Linux 默认pthread底层就是NPTLNative POSIX Threads Library属于glibc 一部分现在直接集成在libc.so.6不需要单独链接-lpthread。它分成两层用户态层pthread 库管理线程栈、TLS线程局部存储、互斥锁、条件变量提供pthread_create/join/mutexAPI内核态层Linux 调度器用clone()系统调用创建轻量级进程调度、信号、CPU 时间片管理pthread_create 原理pthread_create不是系统调用它是封装函数底层只做 4 件事步骤 1分配线程栈 TLS用mmap分配独立栈默认 8MB初始化线程局部存储TLS设置线程控制块TCB步骤 2调用 clone () 系统调用真正创建内核线程这是底层唯一核心系统调用clone( CLONE_VM | // 共享虚拟内存 CLONE_FS | // 共享文件系统信息 CLONE_FILES | // 共享文件描述符 CLONE_SIGHAND |// 共享信号处理 CLONE_THREAD | // 归属同一个线程组 CLONE_SYSVSEM // 共享信号量 )共享资源 线程不共享 进程步骤 3内核创建 task_structLWP内核创建新任务PID 不同但TGID线程组 ID 主线程 PIDps -eLf看到的 LWP 就是内核调度实体步骤 4跳转到线程入口函数执行从内核返回用户态执行你传入的start_routine结束后自动调用pthread_exit()内核视角线程 进程1:1 模型Linux 是1:1 线程模型1 个 pthread 线程 1 个 LWP轻量级进程内核完全平等调度所有线程没有内核多对一、多路复用你可以验证ps -eLf | grep 进程名每一行都是一个内核调度实体。pthread 同步机制底层mutex/cond这是面试 / 底层开发必考1互斥锁 pthread_mutex_t底层 futex快速用户态互斥锁无竞争时完全在用户态原子操作CAS不进内核有竞争时调用futex()系统调用让内核挂起线程流程用户态原子锁 → 成功 → 直接返回 用户态原子锁 → 失败 → futex(WAIT) 进内核阻塞 解锁 → futex(WAKE) 唤醒极致高效2条件变量 pthread_cond_t底层也是futex配合 mutex 实现等待 / 唤醒。3自旋锁 pthread_spinlock_t用户态循环 CAS不睡眠不进入内核。线程栈与 TLS 底层线程栈mmap(MAP_PRIVATE | MAP_ANONYMOUS)分配TLS线程局部存储每个线程有独立的%fs/%gs段寄存器errno就是 TLS 变量所以线程安全pthread 与进程的真正区别资源进程fork线程pthread虚拟内存独立共享文件描述符独立共享信号处理独立共享栈空间独立独立内核实体task_structtask_struct创建调用fork()clone()唯一本质区别共享的资源多少。关键底层系统调用清单pthread 所有功能都依赖这 3 个系统调用clone()创建线程futex()互斥锁、条件变量、同步rt_sigaction()线程信号处理futexfutex Fast Userspace Mutex快速用户态互斥锁内核只负责阻塞 / 唤醒用户态负责抢锁。它是pthread_mutex / pthread_cond / sem_t / rwlock的唯一底层基石混合机制用户态原子操作 内核态等待队列无竞争时0 系统调用、0 内核切换、极致快有竞争时才进入内核休眠 / 唤醒futex 本质结构一个 futex 就是一个 32 位整型变量uint32_t放在用户态内存。uint32_t futex;内核不存储futex 值只根据这个地址维护等待队列hash 表提供 2 个核心操作WAIT / WAKE唯一系统调用sys_futex所有功能都通过同一个系统调用靠命令区分long futex( uint32_t *uaddr, // 用户态 futex 地址关键 int futex_op, // 操作WAIT / WAKE / REQUEUE 等 uint32_t val, // 期望的值 / 唤醒数量 ... );最核心 2 个操作FUTEX_WAIT如果*uaddr val阻塞当前线程加入等待队列不等于 → 直接返回避免 “睡错了”FUTEX_WAKE唤醒最多val个在uaddr上等待的线程futex 工作流程pthread_mutex 真实底层这是真实 glibc NPTL 代码逻辑不是简化版。1. 加锁无竞争完全用户态if (cmpxchg(futex, 0, 1) 0) { // 抢到锁直接返回**不进内核** return 0; }✅无竞争0 系统调用极致快2. 锁已被占用有竞争进入内核// 将 futex 值设为 2表示有人等待 if (cmpxchg(futex, 1, 2) ! 0) { // 可能锁已释放重试 } // 内核如果 *uaddr 2就阻塞 futex(futex, FUTEX_WAIT, 2, ...);此时线程进入 TASK_INTERRUPTIBLE让出 CPU。3. 解锁if (atomic_dec(futex) ! 0) { // 有等待者唤醒 1 个 futex(futex, FUTEX_WAKE, 1, ...); }为什么 futex 是革命性设计传统锁用户态自旋锁 → 占用 CPU高竞争浪费纯系统调用sys_sync→ 每次都进内核极慢futex无竞争用户态 CAS完全不陷内核有竞争内核阻塞休眠不占 CPU→ 兼顾速度与效率futex 与 pthread 关系pthread_mutex_t ↓ futex32位原子变量 ↓ 用户态 CAS快路径 ↓ 竞争 futex(WAIT/WAKE) 系统调用慢路径 ↓ 内核等待队列 调度条件变量 pthread_cond_t 也是 futexcond_wait释放 mutex futex_waitcond_signalfutex_wake总结pthread 是库不是内核功能底层是 NPTL glibc线程 轻量级进程LWP内核 1:1 调度pthread_create 分配栈 clone()系统调用线程共享内存、fd、信号独享栈、TLS、寄存器同步锁mutex/cond底层 futex用户态 内核态混合现代 Linux 中 pthread 直接集成在libc.so无需-lpthread

更多文章