Tamanho de pilha padrão para pthreads

20

Pelo que entendi, o tamanho padrão da pilha para um pthread no Linux é 16K. Estou recebendo resultados estranhos na minha instalação do Ubuntu de 64 bits.

$ ulimit -s
8192

Além disso:

pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Thread stack size = %d bytes \n", stacksize);

Prints
    Thread stack size = 8388608 bytes

Tenho certeza de que o tamanho da pilha não é "8388608". O que poderia estar errado?

    
por Kamath 02.05.2014 / 18:43

2 respostas

18
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

The stacksize attribute shall define the minimum stack size (in bytes) allocated for the created threads stack.

No seu exemplo, o tamanho da pilha é definido como 8388608 bytes, o que corresponde a 8MB, conforme retornado pelo comando ulimit -s Então isso corresponde.

A partir da descrição de pthread_create() :

On Linux/x86-32, the default stack size for a new thread is 2 megabytes. Under the NPTL threading implementation, if the RLIMIT_STACK soft resource limit at the time the program started has any value other than "unlimited", then it determines the default stack size of new threads. Using pthread_attr_setstacksize(3), the stack size attribute can be explicitly set in the attr argument used to create a thread, in order to obtain a stack size other than the default.

Assim, o tamanho da pilha de encadeamentos pode ser definido por meio da função set acima ou da propriedade do sistema ulimit . Para o 16k que você está se referindo, não está claro em qual plataforma você viu isso e / ou se algum limite de sistema foi definido para isso.

Veja a página pthread_create e aqui para alguns exemplos interessantes sobre isso.

    
por 02.05.2014 / 21:33
29

Na verdade, o tamanho da pilha virtual é 8388608 bytes (8 MB). Naturalmente, é natural concluir que isso não pode estar certo, porque essa é uma quantidade ridiculamente grande de memória para cada segmento consumir para sua pilha, quando 99% do tempo um par de KB é provavelmente tudo que eles precisam.

A boa notícia é que o seu segmento usa apenas a quantidade de memória física que realmente precisa. Este é um dos poderes mágicos que seu sistema operacional obtém usando a Unidade de Gerenciamento de Memória de hardware (MMU) do seu processador. Veja o que acontece:

  1. O SO aloca 8 MB de memória virtual para sua pilha, configurando as tabelas de páginas da MMU para o seu segmento. Isso requer muito pouca memória RAM para manter apenas as entradas da tabela de páginas.

  2. Quando o thread é executado e tenta acessar um endereço virtual na pilha que ainda não possui uma página física atribuída a ele, uma exceção de hardware chamada "falha de página" é acionada pela MMU.

  3. O núcleo da CPU responde à exceção de falha de página alternando para um modo de execução privilegiada (que tem sua própria pilha) e chamando a função de manipulador de exceção de falha de página dentro do kernel.

  4. O kernel aloca uma página de RAM física para essa página de memória virtual e retorna para o segmento de espaço do usuário.

O encadeamento do espaço do usuário não vê nada disso. Do seu ponto de vista, ele apenas usa a pilha como se a memória estivesse lá o tempo todo. Enquanto isso, a pilha cresce automaticamente (ou não) para atender às necessidades do segmento.

A MMU é uma parte fundamental do hardware dos sistemas de computador atuais. Em particular, ele é responsável por grande parte da "mágica" do sistema, por isso recomendo aprender mais sobre o que a MMU faz e sobre a memória virtual em geral. Além disso, se seu aplicativo é sensível ao desempenho e lida com uma quantidade significativa de dados, você deve entender como funciona o TLB (o cache de tabela de páginas da MMU) e como você pode reestruturar seus dados ou seus algoritmos para maximizar sua taxa de acertos TLB. p>     

por 03.05.2016 / 19:57