You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用pthread实现动态增长栈?基于APUE特性的实现方案咨询

利用栈Guard区实现动态增长栈

核心原理

当线程栈的rsp(x86_64架构)指针低于stackaddr指定的栈基址时,会进入标记为不可访问的Guard栈区域,触发SIGSEGV信号。我们可以通过捕获该信号,动态扩展栈的可用空间、调整Guard区位置,之后让程序继续执行,从而实现栈的自动增长。

实现步骤

  1. 初始化自定义线程栈:使用pthread_attr_setstack和pthread_attr_setguardsize为线程指定初始栈空间,栈底部保留一页大小的Guard区(标记为不可访问)。
  2. 注册SIGSEGV信号处理函数:在处理函数中判断触发信号的地址是否位于Guard区范围内。
  3. 扩展栈空间:重新分配更大的内存块,将原栈数据复制到新栈的对应位置,调整栈基址stackaddr,并将新栈的底部设置为新的Guard区。
  4. 恢复执行:修正线程栈指针(若需要),让程序从触发信号的指令处继续运行。

代码示例

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <ucontext.h>

#define PAGE_SIZE 4096
#define INIT_STACK_SIZE (16 * PAGE_SIZE)
#define STACK_GROW_STEP (16 * PAGE_SIZE)

// 全局变量保存线程栈的当前信息
typedef struct {
    void *stack_base;    // 栈基址(高地址,栈向下生长)
    size_t stack_size;   // 当前栈总大小
    size_t guard_size;   // Guard区大小
} StackInfo;

StackInfo thread_stack_info;

// SIGSEGV信号处理函数
void segv_handler(int sig, siginfo_t *si, void *ctx) {
    ucontext_t *uc = (ucontext_t *)ctx;
    void *fault_addr = si->si_addr;

    // 判断是否是Guard区触发的错误
    void *guard_start = thread_stack_info.stack_base - thread_stack_info.stack_size;
    void *guard_end = guard_start + thread_stack_info.guard_size;
    if (fault_addr >= guard_start && fault_addr < guard_end) {
        // 扩展栈空间
        size_t new_stack_size = thread_stack_info.stack_size + STACK_GROW_STEP;
        void *new_stack_base = mmap(NULL, new_stack_size, PROT_READ | PROT_WRITE, 
                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (new_stack_base == MAP_FAILED) {
            perror("mmap failed");
            exit(EXIT_FAILURE);
        }

        // 复制原栈内容到新栈的对应位置(栈向下生长,原栈数据在靠近基址的区域)
        void *old_stack_top = thread_stack_info.stack_base;
        void *new_stack_top = new_stack_base + new_stack_size;
        size_t copy_size = thread_stack_info.stack_size - thread_stack_info.guard_size;
        memcpy(new_stack_top - copy_size, old_stack_top - copy_size, copy_size);

        // 释放旧栈
        munmap(guard_start, thread_stack_info.stack_size);

        // 更新栈信息
        thread_stack_info.stack_base = new_stack_top;
        thread_stack_info.stack_size = new_stack_size;

        // 设置新的Guard区(底部一页)为不可访问
        if (mprotect(new_stack_base, thread_stack_info.guard_size, PROT_NONE) == -1) {
            perror("mprotect failed");
            exit(EXIT_FAILURE);
        }

        // 调整rsp指针到新栈的对应位置
        uc->uc_mcontext.gregs[REG_RSP] = uc->uc_mcontext.gregs[REG_RSP] + (new_stack_top - old_stack_top);
        return; // 恢复执行
    }

    // 非Guard区的错误,按默认处理
    signal(sig, SIG_DFL);
    raise(sig);
}

// 线程函数,递归调用模拟栈增长
void recursive_func(int depth) {
    char buf[1024]; // 占用栈空间
    printf("Depth %d, rsp = %p\n", depth, &buf);
    recursive_func(depth + 1);
}

int main() {
    // 注册信号处理函数
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = segv_handler;
    sa.sa_flags = SA_SIGINFO;
    if (sigaction(SIGSEGV, &sa, NULL) == -1) {
        perror("sigaction failed");
        exit(EXIT_FAILURE);
    }

    // 初始化线程栈
    thread_stack_info.guard_size = PAGE_SIZE;
    thread_stack_info.stack_size = INIT_STACK_SIZE;
    thread_stack_info.stack_base = mmap(NULL, thread_stack_info.stack_size, 
                                        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (thread_stack_info.stack_base == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }
    thread_stack_info.stack_base += thread_stack_info.stack_size; // 栈基址是高地址

    // 设置Guard区为不可访问
    void *guard_start = thread_stack_info.stack_base - thread_stack_info.stack_size;
    if (mprotect(guard_start, thread_stack_info.guard_size, PROT_NONE) == -1) {
        perror("mprotect failed");
        exit(EXIT_FAILURE);
    }

    // 创建自定义栈的线程
    pthread_t tid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setstack(&attr, guard_start + thread_stack_info.guard_size, 
                          thread_stack_info.stack_size - thread_stack_info.guard_size);
    pthread_attr_setguardsize(&attr, 0); // 禁用系统默认的Guard区,用我们自己的

    if (pthread_create(&tid, &attr, (void *)recursive_func, (void *)0) != 0) {
        perror("pthread_create failed");
        exit(EXIT_FAILURE);
    }

    pthread_join(tid, NULL);
    return 0;
}

代码说明

  • 用mmap分配栈内存,方便精确控制内存属性和大小。
  • 栈向下生长,所以栈基址设为分配内存的高地址。
  • 信号处理函数中,通过判断错误地址是否在Guard区来触发栈扩展。
  • 扩展时复制原栈数据到新栈,调整rsp指针让线程继续在新栈执行。
  • 禁用了pthread默认的Guard区,改用自定义的Guard区实现动态增长逻辑。

内容的提问来源于stack exchange,提问作者Markity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 06:41:19