O que é “expansão automática de pilha”?

10

getrlimit (2) tem a seguinte definição nas páginas man:

RLIMIT_AS O tamanho máximo da memória virtual do processo (espaço de endereço) em bytes. Esse limite afeta as chamadas para brk (2), mmap (2) e mremap (2), que falham com o erro ENOMEM ao exceder esse limite. Também expansão automática da pilha falhará (e gerará um SIGSEGV que mata o processo se nenhuma pilha alternativa tiver sido disponibilizada via sigaltstack (2)). Como o valor é longo, em máquinas com 32 bits, esse limite é no máximo 2 GiB ou esse recurso é ilimitado.

O que significa "expansão automática de pilha" aqui? A pilha em um ambiente Linux / UNIX cresce conforme o necessário? Se sim, qual é o mecanismo exato?

    
por loudandclear 05.02.2013 / 05:39

2 respostas

2

Sim pilhas crescem dinamicamente. A pilha está no topo da memória crescendo para baixo em direção ao heap.

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

O heap cresce para cima (sempre que você faz malloc) e a pilha cresce para baixo à medida que novas funções são chamadas. O heap está presente logo acima da seção BSS do programa. O que significa que o tamanho do seu programa e a maneira como ele aloca memória no heap também afetam o tamanho máximo da pilha para esse processo. Normalmente, o tamanho da pilha é ilimitado (até as áreas de pilha e pilha se encontrarem e / ou sobrescreverem, o que causará um estouro de pilha e SIGSEGV: -)

Isto é apenas para os processos do usuário, A pilha do kernel é sempre corrigida (geralmente 8KB)

    
por 05.02.2013 / 06:45
8

O mecanismo exato é dado aqui, no Linux: em manipulação de uma falha de página em mapeamentos anônimos você verifique se é um "aumento de alocação" que você deve expandir como uma pilha. Se o registro da área VM disser que você deveria, ajuste o endereço inicial para expandir a pilha.

Quando ocorre uma falha de página, dependendo do endereço, ela pode ser reparada (e a falha é anulada) através da expansão da pilha. Esse comportamento de "crescimento para baixo em uma falha" para memória virtual pode ser solicitado por programas de usuário arbitrários com o sinalizador MAP_GROWSDOWN sendo passado para o mmap syscall.

Você também pode mexer com esse mecanismo em um programa do usuário:

#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;
}

Quando ele solicita que você encontre o pid do programa (via ps ) e veja /proc/$THAT_PID/maps para ver como a área original cresceu.

    
por 13.06.2013 / 09:48