我试图了解堆栈分配和对齐如何与带有gcc的Linux x86_64系统上的pthreads以及系统在堆栈中存储的数据一起使用。我知道您可以使用pthread_attr_setstack
配置堆栈内存。我在执行以下操作的测试程序中完成了此操作:
1)递归调用自身并更新在堆栈上分配的未初始化数组
2)打印出第一个数组元素的值,最后一个元素和rsp
由此我已经能够观察到rsp如何递增(在我的测试程序中,我注意到编译器内联了一些递归调用)。我也能够看到添加TLS内存(带有__thread变量)导致rsp的第一个值更低。所以看起来TLS变量分配在堆栈顶部。
然而,我不确定的是还有什么。在我看来,堆栈的第一页是以某种方式为系统保留的,因为我分配的堆栈变量都没有在该区域中结束。即使我不使用任何__thread变量,我实例化的变量似乎也没有在第一页中分配(我设置堆栈内存使其与页面对齐)。
所以我的问题是:除了TLS数据和堆栈变量之外,还有什么东西,如果有的话,还有pthread?
答案 0 :(得分:4)
在Linux NPTL上:
堆栈的最顶层包含TCB。这也称为struct pthread
或pthread_t
。由于所有奇怪的glibc系统特定的定义,它有点毛茸茸,但基本上它包含如下内容:
pthread_cleanup
并堆叠展开信息pthread_setspecific
使用的一定数量的TLS插槽 - 如果超过此数量,其余部分将在堆上分配pthread_join
在新线程实际开始运行之前,这主要是在pthread_create
(__pthread_create_2_1
nptl/pthread_create.c
__thread
中初始化的,如果你在eglibc源代码中跟随)。
TCB下面是静态分配的l_tls_offset
变量 - 或者至少是链接器在启动时可以识别的变量。动态链接器初始化链接器映射中的__thread
字段,以告知NPTL代码要保留多少空间。请注意,在程序启动后加载的库不会成为其中的一部分 - 有关详细信息,请参阅__thread
ABI spec。
start_thread()
变量下面是堆栈。这个堆栈的顶部是{{1}}代码,所以它在实际执行用户代码之前仍然是一种方式(但不会太多)。
答案 1 :(得分:0)
子线程中的“堆栈”实际上只是一块内存,通常是malloc'd(内部可以通过mmaping匿名区域实现),尽管你也可以使用原始堆栈区域本身,只要它保持有效在线程的生命周期中,例如堆栈分配和对齐如何与Linux x86_64上的pthread一起使用
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);