Stack space for a new thread is created by the parent thread with mmap(MAP_ANONYMOUS|MAP_STACK)
. So they're in the "memory map segment", as your diagram labels it. It can end up anywhere that a large malloc()
could go. (glibc malloc(3)
uses mmap(MAP_ANONYMOUS)
for large allocations.)
(MAP_STACK
is currently a no-op, and exists in case some future architecture needs special handling).
You pass a pointer to the new thread's stack space to the clone(2)
system call which actually creates the thread. (Try using strace -f
on a multi-threaded process sometime). See also this blog post about creating a thread using raw Linux syscalls.
See this answer on a related question for some more details about mmaping stacks. e.g. MAP_GROWSDOWN
doesn't prevent another mmap()
from picking the address right below the thread stack, so you can't depend on it to dynamically grow a small stack the way you can for the main thread's stack (where the kernel reserves the address space even though it's not mapped yet).
So even though mmap(MAP_GROWSDOWN)
was designed for allocating stacks, it's so bad that Ulrich Drepper proposed removing it in 2.6.29.
Also, note that your memory-map diagram is for a 32-bit kernel. A 64-bit kernel doesn't have to reserve any user virtual-address space for mapping kernel memory, so a 32-bit process running on an amd64 kernel can use the full 4GB of virtual address space. (Except for the low 64k by default (sysctl vm.mmap_min_addr = 65536
), so NULL-pointer dereference does actually fault. And the top page is also reserved as error codes, not valid pointers.)
Related:
See Relation between stack limit and threads for more about stack-size for pthreads. getrlimit(RLIMIT_STACK)
is the main thread's stack size. Linux pthreads uses RLIMIT_STACK
as the stack size for new threads, too.