位于进程虚拟地址空间中的其他线程的堆栈在哪里?

Posted

技术标签:

【中文标题】位于进程虚拟地址空间中的其他线程的堆栈在哪里?【英文标题】:Where are the stacks for the other threads located in a process virtual address space? 【发布时间】:2017-12-05 03:01:41 【问题描述】:

下图显示了进程的各个部分在进程的虚拟地址空间中的布局位置(在 Linux 中):

你可以看到只有一个堆栈部分(因为我假设这个进程只有一个线程)。

但是如果这个进程有另一个线程怎么办,第二个线程的堆栈在哪里呢?它会位于第一个堆栈的正下方吗?

【问题讨论】:

C 标签不相关,C 没有定义它们的概念。 另见The Stack ***。 Qualys 设法打破了逻辑内存区域之间的分离。他们可以使用一个内存区域来覆盖另一个区域中的对象。 Linux 尤其容易受到攻击(运行gresecurity patches 的系统除外)。另请参阅 OSS-Security 邮件列表中的 More CONFIG_VMAP_STACK vulnerabilities。 【参考方案1】:

新线程的堆栈空间由父线程mmap(MAP_ANONYMOUS|MAP_STACK) 创建。因此,它们位于“内存映射段”中,正如您的图表所标记的那样。它可以在一个大的malloc() 可以去的任何地方结束。 (glibc malloc(3) 使用 mmap(MAP_ANONYMOUS) 进行大分配。)

MAP_STACK 目前是空操作,存在于某些未来架构需要特殊处理的情况下)。

您将指向新线程堆栈空间的指针传递给the clone(2) system call,它实际上创建了线程。 (尝试在多线程进程中使用strace -f)。另见this blog post about creating a thread using raw Linux syscalls。

有关映射堆栈的更多详细信息,请参阅this answer on a related question。例如MAP_GROWSDOWN 不会阻止另一个 mmap() 选择线程堆栈正下方的地址,因此您不能像主线程堆栈那样依赖它来动态增长一个小堆栈(内核保留地址空间,即使它尚未映射)。

所以即使mmap(MAP_GROWSDOWN) 是为分配堆栈而设计的,it's so bad that Ulrich Drepper proposed removing it in 2.6.29。


另外,请注意您的内存映射图适用于 32 位内核。 64 位内核不必为映射内核内存保留任何用户虚拟地址空间,因此在 amd64 内核上运行的 32 位进程可以使用完整的 4GB 虚拟地址空间。 (除了默认的低 64k (sysctl vm.mmap_min_addr = 65536),所以 NULL 指针取消引用实际上是错误的。并且the top page is also reserved as error codes,不是有效的指针。)


相关:

有关 pthread 的堆栈大小的更多信息,请参阅 Relation between stack limit and threads。 getrlimit(RLIMIT_STACK) 是主线程的堆栈大小。 Linux pthreads 也使用RLIMIT_STACK 作为新线程的堆栈大小。

【讨论】:

以上是关于位于进程虚拟地址空间中的其他线程的堆栈在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

多线程介绍

进程和线程(待续)

关于go中并行的初步理解

进程与线程的问题

进程的虚拟地址空间,堆栈堆数据段代码段

进程和线程的区别