Estou analisando este livro , Programação Avançada em Linux por Mark Mitchell, Jeffrey Oldham e Alex Samuel. É de 2001, um pouco velho. Mas acho isso muito bom de qualquer maneira.
No entanto, cheguei a um ponto em que ele diverge do que meu Linux produz na saída do shell. Na página 92 (116 no visualizador), o capítulo 4.5 Implementação de Thread GNU / Linux começa com o parágrafo que contém esta declaração:
A implementação de threads POSIX no GNU / Linux difere da implementação de threads em muitos outros sistemas semelhantes ao UNIX de uma maneira importante: no GNU / Linux, os threads são implementados como processos.
Este parece ser um ponto-chave e é posteriormente ilustrado com um código C. A saída do livro é:
main thread pid is 14608
child thread pid is 14610
E no meu Ubuntu 16.04 é:
main thread pid is 3615
child thread pid is 3615
ps
saída suporta isso.
Eu acho que algo deve ter mudado entre 2001 e agora.
O próximo subcapítulo na próxima página, 4.5.1 Manipulação de sinais, se baseia na instrução anterior:
O comportamento da interação entre sinais e threads varia de um sistema semelhante ao UNIX para outro. No GNU / Linux, o comportamento é ditado pelo fato de os threads serem implementados como processos.
E parece que isso será ainda mais importante mais adiante neste livro. Alguém poderia explicar o que está acontecendo aqui?
Eu já vi este. Os threads do kernel do Linux são realmente processos do kernel? , mas isso não ajuda muito. Estou confuso.
Este é o código C:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_function (void* arg)
{
fprintf (stderr, "child thread pid is %d\n", (int) getpid ());
/* Spin forever. */
while (1);
return NULL;
}
int main ()
{
pthread_t thread;
fprintf (stderr, "main thread pid is %d\n", (int) getpid ());
pthread_create (&thread, NULL, &thread_function, NULL);
/* Spin forever. */
while (1);
return 0;
}
getpid
retorna o que seria chamado de ID do grupo de encadeamentos e para obter um ID exclusivo para um processo que você precisa usargettid
. No entanto, além do kernel, a maioria das pessoas e ferramentas chama um grupo de encadeamentos de processo e chama um processo de encadeamento, por consistência com outros sistemas.Respostas:
Eu acho que essa parte da
clone(2)
página de manual pode esclarecer a diferença. o PID:A frase "threads são implementados como processos" refere-se ao problema de threads que tiveram PIDs separados no passado. Basicamente, o Linux originalmente não tinha threads em um processo, apenas processos separados (com PIDs separados) que poderiam ter alguns recursos compartilhados, como memória virtual ou descritores de arquivo.
CLONE_THREAD
ea separação do ID do processo (*) e do ID do encadeamento fazem com que o comportamento do Linux se pareça mais com outros sistemas e mais com os requisitos do POSIX nesse sentido. Embora tecnicamente o sistema operacional ainda não tenha implementações separadas para threads e processos.O manuseio de sinais foi outra área problemática da antiga implementação. Isso é descrito com mais detalhes no documento que @FooF se refere em sua resposta .
Conforme observado nos comentários, o Linux 2.4 também foi lançado em 2001, no mesmo ano do livro, portanto, não é de surpreender que as notícias não cheguem a essa impressão.
fonte
Você está certo, de fato "algo deve ter mudado entre 2001 e agora". O livro que você está lendo descreve o mundo de acordo com a primeira implementação histórica de threads POSIX no Linux, chamada LinuxThreads (consulte também o artigo da Wikipedia para alguns).
O LinuxThreads teve alguns problemas de compatibilidade com o padrão POSIX - por exemplo, threads que não compartilham PIDs - e alguns outros problemas sérios. Para corrigir essas falhas, outra implementação chamada NPTL (Native POSIX Thread Library) foi liderada pela Red Hat para adicionar o suporte necessário à biblioteca do kernel e do espaço do usuário para alcançar uma melhor conformidade com o POSIX (tirando boas partes de outro projeto de reimplementação da IBM chamado NGPT (" Threads Posix da próxima geração "), consulte o artigo da Wikipedia sobre NPTL ). Os sinalizadores adicionais adicionados à
clone(2)
chamada do sistema (principalmente osCLONE_THREAD
que@ikkkachu
apontam em sua resposta ) são provavelmente a parte mais evidente das modificações do kernel. A parte do espaço do usuário do trabalho acabou sendo incorporada à GNU C Library.Ainda hoje em dia, alguns SDKs Linux embarcados usam a antiga implementação LinuxThreads porque estão usando uma versão menor da LibC, chamada uClibc (também chamada µClibc) , e levou um período considerável de anos até que a implementação do espaço do usuário NPTL do GNU LibC fosse portada e assumida como implementação de encadeamento padrão do POSIX, como geralmente essas plataformas especiais não se esforçam para seguir as novas modas com a velocidade da luz. Isso pode ser observado ao observar que, de fato, os PIDs para diferentes threads nessas plataformas também são diferentes, diferentemente do padrão do POSIX especificado - assim como o livro que você está lendo descreve. Na verdade, uma vez que você ligou
pthread_create()
, você subitamente aumentou a contagem de processos de um para três, pois eram necessários processos adicionais para manter a bagunça unida.A página de manual do Linux pthreads (7) fornece uma visão geral abrangente e interessante das diferenças entre os dois. Outra descrição esclarecedora, embora desatualizada, das diferenças é este artigo de Ulrich Depper e Ingo Molnar sobre o design do NPTL.
Eu recomendo que você não leve essa parte do livro muito a sério. Em vez disso, recomendo os tópicos de programação do POSIX de Butenhof e as páginas de manual do POSIX e Linux sobre o assunto. Muitos tutoriais sobre o assunto são imprecisos.
fonte
Os encadeamentos (espaço de usuário) não são implementados como processos no Linux, na medida em que não possuem seu próprio espaço de endereço privado, eles ainda compartilham o espaço de endereço do processo pai.
No entanto, esses encadeamentos são implementados para usar o sistema de contabilidade de processo do kernel; portanto, eles recebem seu próprio ID de encadeamento (TID), mas recebem o mesmo PID e 'ID do grupo de encadeamentos (TGID) que o processo pai - isso contrasta com um fork, onde um novo TGID e PID são criados, e o TID é o mesmo que o PID.
Portanto, parece que os kernels recentes tinham um TID separado que pode ser consultado; é diferente para os threads; um trecho de código adequado para mostrar isso em cada uma das principais () threads_funções () acima é:
Portanto, o código inteiro com isso é:
Fornecendo um exemplo de saída de:
fonte
Basicamente, as informações em seu livro são historicamente precisas, devido a um histórico de implementação vergonhosamente ruim de threads no Linux. Esta resposta minha para uma pergunta relacionada ao SO também serve como resposta para sua pergunta:
https://stackoverflow.com/questions/9154671/distinction-between-processes-and-threads-in-linux/9154725#9154725
fonte
Internamente, não existem processos ou threads no kernel do linux. Processos e threads são um conceito principalmente de usuário, o próprio kernel vê apenas "tarefas", que são um objeto escalonável que pode compartilhar nenhum, alguns ou todos os seus recursos com outras tarefas. Os encadeamentos são tarefas configuradas para compartilhar a maioria de seus recursos (espaço de endereço, mmaps, pipes, manipuladores de arquivos abertos, soquetes etc.) com a tarefa pai, e processos são tarefas configuradas para compartilhar recursos mínimos com a tarefa pai .
Ao usar a API do Linux diretamente ( clone () , em vez de fork () e pthread_create () ), você tem muito mais flexibilidade para definir a quantidade de recursos que deseja compartilhar ou não e pode criar tarefas que não são totalmente processo nem totalmente um thread. Se você usar essas chamadas de baixo nível diretamente, também será possível criar uma tarefa com um novo TGID (assim tratado como um processo pela maioria das ferramentas da terra do usuário) que compartilhe todos os seus recursos com a tarefa pai ou vice-versa, para criar uma tarefa com TGID compartilhado (assim tratado como um encadeamento pela maioria das ferramentas da terra do usuário) que não compartilha nenhum recurso com sua tarefa pai.
Enquanto o Linux 2.4 implementa o TGID, isso é principalmente para o benefício da contabilidade de recursos. Muitos usuários e a ferramenta de espaço de usuário acham útil poder agrupar tarefas relacionadas e relatar o uso de recursos.
A implementação de tarefas no Linux é muito mais fluida do que a visão de mundo dos processos e threads apresentada pelas ferramentas de espaço do usuário.
fonte
Linus Torvalds declarou em uma lista de discussão do kernel postada em 1996 que “tanto os threads quanto os processos são tratados como um 'contexto de execução'", que é "apenas um conglomerado de todo o estado desse CoE .... inclui coisas como CPU estado, estado da MMU, permissões e vários estados de comunicação (arquivos abertos, manipuladores de sinais, etc) ".
Como você pode ver, este programa gerará 25 threads de uma só vez, cada um dos quais dormirá por 100 segundos e depois ingressará no programa principal novamente. Depois que todos os 25 threads retornaram ao programa, o programa está concluído e será encerrado.
Usando
top
você poderá ver 25 instâncias do programa "threads2". Mas é muito chato. A saída deps auwx
é ainda menos interessante ... MASps -eLf
fica meio emocionante.Você pode ver aqui todos os 26 CoEs que o
thread2
programa criou. Todos eles compartilham o mesmo ID do processo (PID) e ID do processo pai (PPID), mas cada um tem um ID LWP diferente (processo leve), e o número de LWPs (NLWP) indica que existem 26 CoEs - o programa principal e o 25 tópicos gerados por ele.fonte
Quando se trata de processos e threads Linux são espécie da mesma coisa. O que quer dizer que eles são criados com a mesma chamada de sistema:
clone
.Se você pensar bem, a diferença entre threads e processos está em que objetos do kernel serão compartilhados pelo filho e pelo pai. Para processos, não é muito: descritores de arquivos abertos, segmentos de memória que não foram gravados, provavelmente alguns outros nos quais não consigo pensar em detalhes. Para threads, muito mais objetos são compartilhados, mas não todos.
O que aproxima threads e objetos no Linux é a
unshare
chamada do sistema. Os objetos do kernel que começam como compartilhados podem não ser compartilhados após a criação do thread. Assim, você pode, por exemplo, ter dois encadeamentos do mesmo processo que possuem espaço diferente para o descritor de arquivo (revogando o compartilhamento de descritores de arquivo após a criação dos encadeamentos). Você pode testá-lo criando um encadeamento, chamandounshare
nos dois encadeamentos e fechando todos os arquivos e abrindo novos arquivos, canais ou objetos nos dois encadeamentos. Então olhe para dentro/proc/your_proc_fd/task/*/fd
e verá que cada umtask
(que você criou como um thread) terá diferentes fd's.De fato, tanto a criação de novos threads quanto de novos processos são rotinas de bibliotecas que chamam
clone
por baixo e especificam quais objetos do kernel o processo-thread-thingamajig recém criado (ou seja,task
) compartilhará com o processo / thread de chamada.fonte