Recentemente, fui perguntado em uma entrevista qual é a diferença entre um processo e um tópico. Realmente, eu não sabia a resposta. Pensei por um minuto e dei uma resposta muito estranha.
Threads compartilham a mesma memória, processos não. Depois de responder a isso, o entrevistador me deu um sorriso maligno e disparou as seguintes perguntas para mim:
Q. Você conhece os segmentos nos quais um programa é dividido?
Minha resposta: sim (pensei que era fácil) Stack, Data, Code, Heap
P. Então, diga-me: quais segmentos os threads compartilham?
Não pude responder e acabei dizendo todas elas.
Por favor, alguém pode apresentar as respostas corretas e impressionantes para a diferença entre um processo e um tópico?
Respostas:
Você está praticamente correto, mas os threads compartilham todos os segmentos, exceto a pilha. Os encadeamentos têm pilhas de chamadas independentes, no entanto, a memória em outras pilhas de encadeamentos ainda está acessível e, em teoria, você pode manter um ponteiro para a memória no quadro de pilha local de algum outro encadeamento (embora você provavelmente encontre um lugar melhor para colocar essa memória!).
fonte
Da Wikipedia (acho que seria uma resposta muito boa para o entrevistador: P)
fonte
Algo que realmente precisa ser destacado é que há realmente dois aspectos nessa questão - o aspecto teórico e o de implementação.
Primeiro, vamos olhar para o aspecto teórico. Você precisa entender o que é um processo conceitualmente para entender a diferença entre um processo e um encadeamento e o que é compartilhado entre eles.
Temos o seguinte na seção 2.2.2 O modelo de encadeamento clássico nos sistemas operacionais modernos 3e de Tanenbaum:
Ele continua:
Mais abaixo, ele fornece a seguinte tabela:
O acima é o que você precisa para que os threads funcionem. Como outros já apontaram, coisas como segmentos são detalhes de implementação dependentes do SO.
fonte
Diga ao entrevistador que isso depende inteiramente da implementação do sistema operacional.
Veja o Windows x86, por exemplo. Existem apenas 2 segmentos [1], Código e Dados. E eles são mapeados para todo o espaço de endereço de 2 GB (linear, usuário). Base = 0, Limite = 2 GB. Eles teriam feito um, mas o x86 não permite que um segmento seja de leitura / gravação e execução. Então eles formaram dois e colocaram CS para apontar para o descritor de código, e o restante (DS, ES, SS, etc) para apontar para o outro [2]. Mas ambos apontam para a mesma coisa!
A pessoa que entrevistou você fez uma suposição oculta de que ele / ela não declarou, e esse é um truque estúpido.
Então, com relação a
Os segmentos são irrelevantes para a questão, pelo menos no Windows. Threads compartilham todo o espaço de endereço. Existe apenas um segmento de pilha, SS, e aponta exatamente para as mesmas coisas que DS, ES e CS fazem [2]. Ou seja, todo o maldito espaço do usuário . 0-2GB. Obviamente, isso não significa que os threads tenham apenas 1 pilha. Naturalmente, cada um tem sua própria pilha, mas os segmentos x86 não são usados para essa finalidade.
Talvez o * nix faça algo diferente. Quem sabe. A premissa em que a pergunta se baseava foi quebrada.
ntsd notepad
:cs=001b ss=0023 ds=0023 es=0023
fonte
Geralmente, os Threads são chamados de processo leve. Se dividirmos a memória em três seções, será: Código, dados e Pilha. Todo processo tem seu próprio código, dados e seções de pilha e, devido a esse contexto, o tempo de troca é um pouco alto. Para reduzir o tempo de alternância de contexto, as pessoas criaram o conceito de encadeamento, que compartilha o segmento de dados e código com outro encadeamento / processo e possui seu próprio segmento STACK.
fonte
Um processo possui segmentos de código, dados, pilha e pilha. Agora, o Ponteiro de Instrução (IP) de um thread OU threads aponta para o segmento de código do processo. Os segmentos de dados e heap são compartilhados por todos os threads. Agora, e a área da pilha? Qual é realmente a área da pilha? É uma área criada pelo processo apenas para que seu encadeamento seja usado ... porque as pilhas podem ser usadas de uma maneira muito mais rápida que as pilhas etc. A área de empilhamento do processo é dividida entre encadeamentos, ou seja, se houver três encadeamentos, A área da pilha do processo é dividida em 3 partes e cada uma é dada aos 3 threads. Em outras palavras, quando dizemos que cada thread tem sua própria pilha, essa pilha é na verdade uma parte da área da pilha do processo alocada para cada thread. Quando um encadeamento termina sua execução, a pilha do encadeamento é recuperada pelo processo. De fato, não apenas a pilha de um processo é dividida entre os threads, mas todo o conjunto de registros que um thread usa como SP, PC e state state são os registros do processo. Portanto, quando se trata de compartilhamento, as áreas de código, dados e heap são compartilhadas, enquanto a área de pilha é apenas dividida entre threads.
fonte
Os encadeamentos compartilham o código, os segmentos de dados e o heap, mas não compartilham a pilha.
fonte
Os threads compartilham dados e código, enquanto os processos não. A pilha não é compartilhada para ambos.
Os processos também podem compartilhar memória, mais precisamente codificar, por exemplo, após a
Fork()
, mas este é um detalhe de implementação e otimização (do sistema operacional). O código compartilhado por vários processos (espero) será duplicado na primeira gravação no código - isso é conhecido como cópia na gravação . Não tenho certeza sobre a semântica exata do código de threads, mas assumo o código compartilhado.1 O código é logicamente privado, mas pode ser compartilhado por motivos de desempenho. 2 Não tenho 100% de certeza.
fonte
Tópicos compartilham tudo [1]. Há um espaço de endereço para todo o processo.
Cada encadeamento possui sua própria pilha e registradores, mas as pilhas de todos os encadeamentos são visíveis no espaço de endereço compartilhado.
Se um segmento alocar algum objeto em sua pilha e enviar o endereço para outro segmento, os dois terão acesso igual a esse objeto.
Na verdade, acabei de perceber um problema mais amplo: acho que você está confundindo dois usos da palavra segmento .
O formato do arquivo de um executável (por exemplo, ELF) possui seções distintas, que podem ser chamadas de segmentos, contendo código compilado (texto), dados inicializados, símbolos de vinculador, informações de depuração etc. Não há segmentos de pilha ou pilha aqui, já que essas são construções apenas em tempo de execução.
Esses segmentos de arquivos binários podem ser mapeados no espaço de endereço do processo separadamente, com permissões diferentes (por exemplo, executável somente leitura para código / texto e não-executável de copiar na gravação para dados inicializados).
Áreas desse espaço de endereço são usadas para diferentes propósitos, como alocação de heap e pilhas de encadeamentos, por convenção (imposta pelas bibliotecas de tempo de execução do idioma). É tudo apenas memória e provavelmente não é segmentado, a menos que você esteja executando no modo 8086 virtual. A pilha de cada encadeamento é um pedaço de memória alocado no momento da criação do encadeamento, com o endereço superior da pilha atual armazenado em um registro de ponteiro de pilha e cada encadeamento mantém seu próprio ponteiro de pilha junto com seus outros registradores.
[1] OK, eu sei: máscaras de sinal, TSS / TSD etc. O espaço de endereço, incluindo todos os seus segmentos de programa mapeados, ainda é compartilhado.
fonte
Em uma estrutura x86, é possível dividir o máximo de segmentos (até 2 ^ 16-1). As diretivas ASM SEGMENT / ENDS permitem isso, e os operadores SEG e OFFSET permitem a inicialização dos registros de segmento. CS: IP geralmente são inicializados pelo carregador, mas para DS, ES, SS o aplicativo é responsável pela inicialização. Muitos ambientes permitem as chamadas "definições de segmento simplificadas", como .code, .data, .bss, .stack etc. e, dependendo também do "modelo de memória" (pequeno, grande, compacto etc.), o carregador inicializa os registros de segmento adequadamente. Normalmente, dados, .bss, .stack e outros segmentos usuais (não faço isso há 20 anos, não lembro de todos) são agrupados em um único grupo - é por isso que geralmente DS, ES e SS apontam para o mesma área, mas isso é apenas para simplificar as coisas.
Em geral, todos os registradores de segmento podem ter valores diferentes no tempo de execução. Portanto, a pergunta da entrevista estava correta: qual dos CODE, DATA e STACK são compartilhados entre os threads. O gerenciamento de heap é outra coisa - é simplesmente uma sequência de chamadas para o sistema operacional. Mas e se você não tiver um sistema operacional, como em um sistema incorporado - você ainda pode ter um novo / excluir no seu código?
Meu conselho para os jovens - leia um bom livro de programação de montagem. Parece que os currículos universitários são bastante pobres nesse aspecto.
fonte
Trecho de: Interface de programação Linux: Manual de programação de sistemas Linux e UNIX, Michael Kerrisk , página 619
fonte
Thread compartilha o heap (há uma pesquisa sobre heap específico de thread), mas a implementação atual compartilha o heap. (e, claro, o código)
fonte
No processo, todos os threads compartilham recursos do sistema, como heap Memory etc., enquanto o Thread tem sua própria pilha
Portanto, seu ans deve ser uma pilha de memória que todos os threads compartilhem para um processo.
fonte