Linux – Do memory mapping segment and heap grow until they meet each other

linuxprocessvirtual-memory

I try to figure out the range of the shared memory segment i.e. the memory mapping segment in the memory layout of a process, from two sources below.


From https://manybutfinite.com/post/anatomy-of-a-program-in-memory/, I found a diagram of memory layout of a process

Do memory mapping segment and heap grow until they meet each other?

Or is there a limit for each of the two segments to grow up to, similar to RLIMIT_STACK for the stack segement?

enter image description here


From The Linux Programming Interface

To allow space for heap and stack growth, shared
memory segments are attached starting at the virtual address 0x40000000 . Mapped
mappings (Chapter 49) and shared libraries (Chapters 41 and 42) are also placed in
this area. (There is some variation in the default location at which shared memory
mappings and memory segments are placed, depending on the kernel versions and
the setting of the process’s RLIMIT_STACK resource limit.)
The address 0x40000000 is defined as the kernel constant TASK_UNMAPPED_BASE.

Does the shared memory segment start at TASK_UNMAPPED_BASE and grow upward?

Note that the previous diagram shows the shared memory segment grow downward, so does it grow upward or downward?

enter image description here

Thanks.

Best Answer

There are a couple of limits which apply to the mmap segment and the heap segment. RLIMIT_AS determines the available address space, overall; this covers all the memory allocations that a program can make. RLIMIT_DATA determines the maximum size of the data segment.

The kernel checks stack expansion, mmap, and brk (heap allocation) against these limits. It also checks allocations against potentially conflicting segments, so the segments never end up overwriting each other (see for example the checks performed for heap allocation with brk). If an allocation is impossible, the program is “informed” as appropriate: the C library returns an ENOMEM error for brk, the kernel kills the program with a SIGSEGV if the stack can’t be expanded...

How the shared memory segment (strictly speaking from the kernel perspective, virtual memory area) grows depends on the memory layout, which can very from one process to another. On most architectures, “legacy” memory maps result in bottom-up allocation, starting from TASK_UNMAPPED_BASE; non-legacy memory maps result in top-down allocation. See arch_pick_mmap_layout() and mmap_is_legacy() in the x86 architecture for an example. You can switch to the legacy memory map using setarch’s -L flag (see below for an example).

In practice, you can see how the segment grows by looking at /proc/$$/maps and checking the addresses of the loaded shared libraries (if any), v. their load order. The dynamic linker is always loaded first; if it has a lower address than other libraries, then allocation is bottom-up, otherwise it’s top-down. Compare the output of cat /proc/self/maps and setarch x86_64 -L cat /proc/self/maps on a 64-bit x86 system.

Related Question