下图显示了进程的部分在进程的虚拟地址空间(在Linux中)的布局位置:
你可以看到只有一个堆栈部分(因为这个过程只有一个我假设的线程)。
但是,如果这个进程有另一个线程,那么第二个线程的堆栈将位于何处?它会位于第一个堆栈的正下方吗?
答案 0 :(得分:6)
新线程的堆栈空间由父线程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。
有关mmaping堆栈的更多详细信息,请参阅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指针取消引用实际上是错误的。)
相关:
有关pthreads的堆栈大小的更多信息,请参阅Relation between stack limit and threads。 getrlimit(RLIMIT_STACK)
是主线程的堆栈大小。 Linux pthreads也使用RLIMIT_STACK
作为新线程的堆栈大小。
答案 1 :(得分:-2)
据我所知,专用于进程堆栈的空间被分成较小的部分,每个部分由给定的线程使用。中间还有一些防护页面,以防止意外粉碎。是的,堆栈低于其他堆栈......