Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

getrlimit(2) has the following definition in the man pages:

RLIMIT_AS The maximum size of the process's virtual memory (address space) in bytes. This limit affects calls to brk(2), mmap(2) and mremap(2), which fail with the error ENOMEM upon exceeding this limit. Also automatic stack expansion will fail (and generate a SIGSEGV that kills the process if no alternate stack has been made available via sigaltstack(2)). Since the value is a long, on machines with a 32-bit long either this limit is at most 2 GiB, or this resource is unlimited.

What is meant by "automatic stack expansion" here? Does the stack in a Linux/UNIX environment grow as needed? If yes, what's the exact mechanism?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

Yes stacks grow dynamically. The stack is in the top of the memory growing downwards towards the heap.

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .

The heap grows upwards (whenever you do malloc) and the stack grows downwards as and when new functions are called. The heap is present just above the BSS section of the program. Which means the size of your program and the way it allcates memory in heap also affect the maximum stack size for that process. Usually the stack size is unlimited (till heap and stack areas meet and/or overwrite which will give a stack overflow and SIGSEGV :-)

This is only for the user processes, The kernel stack is fixed always (usually 8KB)

share|improve this answer
    
"Usually the stack size is unlimited", no, usually it limited by 8Mb (ulimit -s). –  Eddy_Em Feb 5 '13 at 6:57
    
yes you are right in most systems. You are checking the shell's ulimit command, if so there is a hard limit on the stack size, which is unlimited (ulimit -Hs). Anyway, that point was to stress that stack and heap grows in the opposite directions. –  Santosh Feb 5 '13 at 9:53
    
Then how is "automatic expansion" is any different from "pushing elements to the stack"? From your explanation, I get the feeling that they are the same. Also, I felt like starting points of stack and heap are way more than 8MB, so the stack can grow as much as it need (or it hits the heap). Is that true? If yes, how does the operating system decided on where to place the heap and the stack? –  loudandclear Feb 5 '13 at 15:21
    
They are the same, but stacks dont have a fixed size unless you hard limit the size using rlimit. Stack is placed at the end of the virtual memory area, and heap is immediatly af*er tke data segment of the executable. –  Santosh Feb 5 '13 at 21:57
    
I understand, thanks. However, I don't think I get the "stacks don't have fixed size" part. If that's the case, what's the 8Mb soft limit for? –  loudandclear Feb 6 '13 at 16:43

The exact mechanism is given here, on Linux: in handling a page fault on anonymous mappings you check to see whether it's a "grown does allocation" that you should expand like a stack. If the VM area record says you should, then you adjust the start address to expand the stack.

When a page fault occurs, depending on the address, it may be serviced (and the fault quashed) via stack expansion. This "growing downwards on a fault" behavior for virtual memory can be requested by arbitrary user programs with the MAP_GROWSDOWN flag being passed to the mmap syscall.

You can mess around with this mechanism in a user program as well:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
        long page_size = sysconf(_SC_PAGE_SIZE);
        void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        if (MAP_FAILED == mem) {
                perror("failed to create growsdown mapping");
                return EXIT_FAILURE;
        }

        volatile char *tos = (char *) mem + page_size;

        int i;
        for (i = 1; i < 10 * page_size; ++i)
                tos[-i] = 42;

        fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
        (void) getchar();

        if (munmap(mem, page_size))
                perror("failed munmap");

        return EXIT_SUCCESS;
}

When it prompts you find the pid of the program (via ps) and look at /proc/$THAT_PID/maps to see how the original area has grown.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.