【问题标题】:What system data is stored on the stack堆栈中存储了哪些系统数据
【发布时间】:2011-12-12 22:51:12
【问题描述】:

我试图了解堆栈分配和对齐如何在具有 gcc 的 Linux x86_64 系统上与 pthread 一起工作,以及系统在堆栈上存储了哪些数据。我知道您可以使用pthread_attr_setstack 配置堆栈内存。我在一个执行以下操作的测试程序中完成了这项工作:

1) 递归调用自身并更新分配在堆栈上的未初始化数组

2) 打印出数组第一个元素、最后一个元素和rsp的值

由此我已经能够观察到 rsp 是如何递增的(在我的测试程序中,我注意到编译器内联了一些递归调用)。我还能够看到添加 TLS 内存(带有 __thread 变量)会导致 rsp 的第一个值更低。所以看起来 TLS 变量是分配在栈顶的。

但是,我不确定还有什么。在我看来,堆栈的第一页以某种方式为系统保留,因为我分配的堆栈变量都没有最终在该区域中。即使我不使用任何 __thread 变量,我实例化的变量似乎也不会在第一页中分配(我设置堆栈内存使其与页面对齐)。

所以我的问题是:除了 TLS 数据和堆栈变量之外,pthread 的堆栈上还有什么(如果有的话)?

【问题讨论】:

    标签: linux gcc pthreads


    【解决方案1】:

    在 Linux NPTL 上:

    堆栈的最顶部包含 TCB。这也称为struct pthreadpthread_t。由于所有奇怪的 glibc 系统特定定义,这有点麻烦,但基本上它包含以下内容:

    • TLS 标头(这部分不包括实际的 TLS 值)
    • 各种标志
    • 用于跟踪缓存堆栈的链表
    • TID(和堆栈使用中标志)
    • PID
    • 强大的互斥体跟踪信息
    • pthread_cleanup 和堆栈展开信息
    • 取消状态
    • 更多标志
    • pthread_setspecific 使用的一定数量的 TLS 插槽 - 如果超过此数量,其余的将分配在堆上
    • 各种锁
    • pthread_join 的跟踪信息
    • 线程的返回值
    • 调度策略(来自线程属性)
    • 启动例程和线程参数
    • 堆栈大小跟踪数据
    • 等等等等

    这主要是在新线程实际开始运行之前在pthread_createnptl/pthread_create.c 中的__pthread_create_2_1)中初始化的。

    在 TCB 下方静态分配了 __thread 变量 - 或者,至少是链接器在启动时可以识别的变量。动态链接器在链接器映射中初始化一个l_tls_offset 字段,以告诉NPTL 代码要保留多少空间。请注意,在程序启动后加载的库不会成为其中的一部分 - 有关详细信息,请参阅 __thread ABI spec

    __thread 变量下方是堆栈。这个堆栈的顶部是start_thread() 代码,所以在它实际执行用户代码之前它仍然是一个方法(但不会太远)。

    【讨论】:

      【解决方案2】:

      在 Linux x86_64 上堆栈分配和对齐如何与 pthread 配合使用

      子线程中的“堆栈”实际上只是一块内存,通常是 malloc 的(内部可以通过映射匿名区域来实现),尽管您也可以使用原始堆栈区域本身,前提是它仍然有效在线程的生命周期内,例如

      char foo[2<<20];
      pthread_attr_t attr;
      pthread_t tid;
      
      pthread_attr_init(&attr);
      pthread_attr_setstack(&attr, foo, sizeof(foo));
      pthread_create(&tid, &attr, func, NULL);
      pthread_join(tid, NULL);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-30
        • 2011-03-03
        • 1970-01-01
        • 2021-09-30
        • 2014-07-21
        • 2012-11-18
        • 2014-03-08
        相关资源
        最近更新 更多