附录:glibc-2.4 pthread 源码简要(了解)

目录

一、线程创建核心流程分析

1、入口函数:__pthread_create_2_1

2、核心数据结构

3、关键函数实现

二、线程创建的核心机制总结

三、关键问题与解决方案

四、代码注释与补充说明


一、线程创建核心流程分析

1、入口函数:__pthread_create_2_1

路径nptl/pthread_create.c

函数签名

int __pthread_create_2_1(
    pthread_t *newthread, 
    const pthread_attr_t *attr,
    void *(*start_routine)(void *),
    void *arg
);

关键代码解析:

线程属性处理

const struct pthread_attr *iattr = (struct pthread_attr *)attr;
if (iattr == NULL)
    iattr = &default_attr; // 使用默认属性
  • 若用户未指定属性,使用全局默认属性default_attr

线程控制块(TCB)分配

struct pthread *pd = NULL;
int err = ALLOCATE_STACK(iattr, &pd); // 分配栈空间并初始化TCB

通过宏ALLOCATE_STACK调用allocate_stack函数,完成以下操作:

  • 申请内存(优先从缓存获取,失败则调用mmap)。

  • 初始化线程描述符(TCB)结构体struct pthread

  • 设置栈保护区域(guardsize)。

TCB关键字段初始化

pd->start_routine = start_routine; // 用户线程函数
pd->arg = arg;                     // 用户参数
pd->flags = ...;                   // 继承父线程调度属性
pd->joinid = (iattr->flags & ATTR_FLAG_DETACHSTATE) ? pd : NULL; // 分离状态标记

调度参数配置

if (attr != NULL && (iattr->flags & ATTR_FLAG_NOTINHERITSCHED)) {
    // 显式设置用户指定的调度策略和优先级
    pd->schedpolicy = iattr->schedpolicy;
    memcpy(&pd->schedparam, &iattr->schedparam, sizeof(struct sched_param));
}

线程创建与启动

err = create_thread(pd, iattr, STACK_VARIABLES_ARGS);
*newthread = (pthread_t)pd; // 返回TCB地址作为线程ID

2、核心数据结构

线程属性结构体 (struct pthread_attr)

struct pthread_attr {
    struct sched_param schedparam; // 调度参数
    int schedpolicy;               // 调度策略(SCHED_OTHER/FIFO/RR)
    int flags;                     // 属性标志位(如分离状态)
    size_t guardsize;              // 栈保护区域大小
    void *stackaddr;               // 用户指定栈地址(可选)
    size_t stacksize;              // 栈大小
    cpu_set_t *cpuset;             // CPU亲和性设置
};

线程控制块 (struct pthread)

struct pthread {
    union {
        tcbhead_t header;          // TCB头部(含TLS信息)
        void *__padding[16];       // 保留字段
    };
    list_t list;                   // 栈缓存链表节点
    pid_t tid;                     // 线程ID
    pid_t pid;                     // 进程ID
    void *(*start_routine)(void *);// 用户线程函数
    void *arg;                     // 用户参数
    void *result;                  // 线程返回值(供pthread_join使用)
    int cancelhandling;            // 取消状态标志位
    struct pthread_key_data *specific[PTHREAD_KEY_1STLEVEL_SIZE]; // TLS数据
    // ... 其他字段(锁、事件、栈信息等)
};

3、关键函数实现

1. allocate_stack: 栈与TCB分配

路径nptl/allocatestack.c

逻辑流程

计算栈大小

size = attr->stacksize ?: __default_stacksize;
size &= ~__static_tls_align_m1; // 对齐调整

内存分配策略

  • 优先从缓存获取:调用get_cached_stack

  • 失败后动态分配

    mem = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  • TCB位置计算

    pd = (struct pthread *)((char *)mem + size - TLS_TCB_SIZE) - 1;

保护区域设置

if (mprotect(guard, guardsize, PROT_NONE) != 0) {
    // 处理保护区域创建失败
}

2. create_thread: 线程启动

核心操作

设置clone标志

int clone_flags = CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGNAL|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID;

调用do_clone

int res = do_clone(pd, attr, clone_flags, start_thread, STACK_VARIABLES_ARGS, stopped);
  • do_clone内部通过汇编宏ARCH_CLONE调用系统调用clone

异常处理:若线程创建失败,释放资源:

__deallocate_stack(pd);

3. do_clone: 系统调用封装

汇编实现(x86_64示例):

ENTRY(__clone)
    /* 参数校验与栈调整 */
    movq %rcx, 8(%rsi)    # 保存用户参数
    movq %rdi, 0(%rsi)    # 保存线程函数地址
    /* 执行clone系统调用 */
    movl $SYS_ify(clone), %eax
    syscall
    testq %rax, %rax
    jl SYSCALL_ERROR_LABEL
    jz L(thread_start)    # 子线程入口

二、线程创建的核心机制总结

  1. 内存管理

    • TCB与栈空间连续分配,TCB位于栈顶或栈底(依赖架构)。

    • 默认栈大小通过__default_stacksize定义(通常为2MB)。

  2. 系统调用集成

    • 通过clone创建轻量级进程,共享地址空间但独立调度。

    • 使用CLONE_SETTLS设置线程本地存储(TLS)。

  3. 扩展性设计:用户态调度器可行性:通过拦截pthread_create和自定义队列,可在用户态实现调度策略(如协程库)。


三、关键问题与解决方案

  1. NUMA架构优化

    • 问题:默认属性可能导致远端内存访问。

    • 建议:显式设置stackaddr或使用mbind绑定内存节点。

  2. 栈溢出防护:通过guardsize创建不可访问的保护页,触发SIGSEGV

  3. 资源泄漏防护:分离线程(detached)自动释放TCB,非分离线程需通过pthread_join回收。


四、代码注释与补充说明

  1. ALLOCATE_STACK宏展开:参数stackaddrstacksize用于回传栈信息。

    #define ALLOCATE_STACK(attr, pd) \
        allocate_stack(attr, pd, &stackaddr, &stacksize)
  2. TLS布局控制

    • TLS_TCB_AT_TP:TCB位于线程指针(TP)地址。

    • TLS_DTV_AT_TP:动态线程向量(DTV)位于TP地址。

  3. 性能优化细节:栈着色(Stack Coloring):通过COLORING_INCREMENT避免多线程栈地址冲突。

        通过以上分析可见,glibc的pthread实现通过分层设计(用户API→运行时库→系统调用)实现了高效的线程管理,同时保留了足够的扩展接口供用户态调度器实现。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值