Tamanho padrão da pilha para pthreads

24

Pelo que entendi, o tamanho da pilha padrão para um pthread no Linux é 16K. Estou obtendo 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 pode estar errado?

Kamath
fonte
7
Eu acho 8388608 / 1024 = 8192.
Cuonglm
6
Você está pensando em 16k por pilhas de kernel de thread . Problema totalmente separado da memória da pilha de espaço do usuário. as pilhas do kernel são pequenas porque não podem ser paginadas ou alocadas com preguiça e precisam ser páginas contíguas na memória física. elinux.org/Kernel_Small_Stacks . Ter um número extremamente alto de threads totais pode ser um problema para o i386, onde o espaço de endereço é limitado, especialmente com pilhas de 8k por padrão para 32 bits.
Peter Cordes

Respostas:

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

O stacksizeatributo deve definir o tamanho mínimo da pilha (em bytes) alocado para a pilha de threads criada.

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

A partir da pthread_create()descrição:

No Linux / x86-32 , o tamanho da pilha padrão para um novo encadeamento é 2 megabytes . Sob a implementação de encadeamento NPTL, se o limite de recurso programável RLIMIT_STACK no momento em que o programa foi iniciado tiver algum valor diferente de "ilimitado", ele determinará o tamanho da pilha padrão dos novos encadeamentos. Usando pthread_attr_setstacksize (3), o atributo de tamanho da pilha pode ser definido explicitamente no argumento attr usado para criar um encadeamento, a fim de obter um tamanho de pilha diferente do padrão.

Portanto, o tamanho da pilha de encadeamentos pode ser definido através da função set acima ou da ulimitpropriedade do sistema. Para os 16k aos quais 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.

fduff
fonte
47

Na verdade, o tamanho da sua pilha virtual é 8388608 bytes (8 MB). Obviamente, é natural concluir que isso não pode estar certo, porque é uma quantidade ridiculamente grande de memória para cada thread consumir para sua pilha quando 99% das vezes alguns KB provavelmente são tudo o que precisam.

A boa notícia é que seu encadeamento usa apenas a quantidade de memória física necessária. Esse é um dos poderes mágicos que o seu sistema operacional obtém ao usar a MMU (Hardware Management Unit) no seu processador. Aqui está o que acontece:

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

  2. Quando seu encadeamento é executado e tenta acessar um endereço virtual na pilha que ainda não possui uma página física atribuída, 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 privilegiado (que possui sua própria pilha) e chamando a função do 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 ao segmento de espaço do usuário.

O encadeamento de espaço do usuário não vê nada desse trabalho. 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 encadeamento.

A MMU é uma parte essencial do hardware dos sistemas de computadores atuais. Em particular, é 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 o TLB (cache da tabela de páginas da MMU) funciona e como você pode reestruturar seus dados ou algoritmos para maximizar sua taxa de acertos no TLB.

jtchitty
fonte