Você já ouviu falar da Lei de Moore, que governou a indústria de semicondutores por quase 60 anos? Você sabia que a Regra de Moore nunca foi uma lei, mas sim uma observação? Para aqueles que não estão familiarizados com esta lei, é assim que ela se expressa.
A lei de Moore observa que o número de transistores em circuitos integrados densos dobra a cada dois anos.
Embora a regra seja escrita em termos eletrônicos, do ponto de vista de um computador, ela indica que o poder computacional dobrará a cada dois anos. Essencialmente, o poder computacional em um único chip aumenta exponencialmente. Não é fantástico?
O incrível é que, desde que essa observação foi feita, ela se provou verdadeira e agora é considerada uma regra.
No entanto, como todos sabemos, as coisas boas nem sempre são gratuitas. Nesse caso, a computação mais poderosa ocorreu às custas do aquecimento excessivo. Quando chegaram, em 2010, os processadores atingiam temperaturas altas o suficiente para cozinhar massas. (Verifique este vídeo do youtube para experimentar você mesmo).
Além disso, à medida que aumentava o número de transistores em um chip, os limites físicos do material começavam a ser atingidos. As forças subatômicas começaram a se tornar ativas neste ponto, tornando difícil manter a trajetória da lei de Moore.
Parecia que os bons tempos haviam acabado, onde os computadores estavam se tornando cada vez mais rápidos. Mas os engenheiros tinham outros planos. Eles começaram a usar vários núcleos poderosos em vez de um único núcleo superpoderoso.
Embora possa ter resolvido o problema de hardware, apresentou um novo desafio para os engenheiros de software. Os engenheiros tiveram que desenvolver um software que aproveitasse os vários núcleos que os computadores agora tinham. Como resultado, nasceu a programação concorrente, muitas vezes conhecida como programação paralela. Threads estão em destaque nesta área de programação. Mas antes de falar de threads devemos entender o que é um processo.
Um Processo Linux é definido como uma instância em execução de um programa.
Portanto, o sistema deve acompanhar vários itens enquanto um programa é executado, incluindo memória de pilha, código-fonte e registradores, entre outras coisas. No Linux, o agregado de todas essas coisas é chamado de processo. Linux é construído sobre uma base de processos.
Para verificar os diferentes processos em execução em sua máquina, tente executar o seguinte comando. Isso executará todos os processos junto com seu ID de processo.
$ ps
Aqui está um instantâneo de amostra.
Por padrão, o comando acima mostra apenas os processos associados ao usuário atual. Para listar todos os processos podemos utilizar o comando com as seguintes opções.
$ ps -aux
Aqui está um instantâneo de amostra.
A aplicação dessas opções extras ao comando ps
também nos fornece algumas informações extras. Aqui está o que as diferentes bandeiras significam -
a
representa todos os usuáriosu
representa o usuário atualx
exibe o processo executado fora do terminal
Também podemos matar alguns processos usando o comando kill
. Aqui está como é usado -
$ kill PID
Aqui PID
é o ID do processo que podemos obter do comando ps
. Cada processo no sistema Linux terá um PID
exclusivo que é usado para identificá-lo. Você pode até usar o comando pidof
para encontrar o PID
do processo.
$ pidof bash
Quando você inicia um programa ou emite um comando, um processo é formado. Ao executar um comando do terminal, você inicia um novo processo. Como um terminal foi utilizado para gerar esse novo processo, dizemos que o processo do terminal iniciou o novo processo de comando. Em outras palavras, o novo processo de comando é filho do processo de terminal.
Todo processo no ecossistema Linux tem um processo pai que o criou. Podemos usar o seguinte comando para verificar o processo pai de um processo com um determinado PID
.
$ ps -o ppid= -p PID
Todos os processos no Linux são, direta ou indiretamente, filhos do processo com PID 1. Isso não é coincidência. O processo com PID 1 é o processo init e é o primeiro processo iniciado pelo sistema no momento da inicialização. Qualquer processo subsequente é criado como filho desse processo.
Assim, temos uma árvore construída a partir dessas relações entre os processos. Isso é chamado de árvore de processo.
Os processos são extremamente úteis no Linux e não poderíamos viver sem eles. Mas há uma desvantagem para eles, ou talvez nenhuma desvantagem, mas apenas a maneira como funcionam. Um procedimento é pesado. Dados, memória e variáveis são transferidos sempre que um novo processo é iniciado. Cada processo que executa o mesmo programa terá sua própria cópia do código-fonte. Como resultado, gerar um grande número de processos não é uma boa ideia.
No entanto, como os processos são um mecanismo para atender a várias solicitações ao mesmo tempo, somos limitados por essa verdade desagradável. Podemos atender apenas um pequeno número de usuários simultâneos que têm muito em comum, pois só podemos iniciar um número restrito de processos em nosso sistema. Considere um servidor da Web que precisa atender a vários usuários simultâneos. Criar um novo processo para cada usuário é uma operação cara. Como resultado, queremos algo menos caro do que o procedimento. Tópicos entram em jogo aqui.
Threads são apenas processos leves. Um thread compartilha memória com seu processo pai e quaisquer threads que ele criou. Devido a essa memória compartilhada, gerar novos threads é mais barato. Isso fornece o benefício extra de comunicação de encadeamento mais rápida e troca de contexto. Usando threads, um processo pode executar várias tarefas simultaneamente.
Em comparação com o número de processos, podemos gerar um número significativo de threads. Em máquinas com vários núcleos, esses threads são executados em paralelo. Ao contrário de gerar muitos processos ou executar todas as tarefas sequencialmente, isso melhora o desempenho geral do programa.
Vamos tentar iniciar nosso primeiro tópico. É importante observar que não podemos iniciar novos threads com o bash. O comando bash só pode ser usado para criar subprocessos. Então, o que faremos é escrever um código C que inicia dois threads. Em seguida, usando o bash, executaremos esse código C como um subprocesso. Então, dois threads serão criados por este novo processo.
Vamos começar a colocar a mão em algum código. Crie um novo arquivo e nomeie-o threads.c
. Vá em frente e abra-o em qualquer um dos seus IDEs favoritos.
A primeira etapa é importar os arquivos de cabeçalho necessários.
#include <pthread.h> #include <stdio.h>
Estaremos criando dois threads, cada um executando a mesma função, mas com parâmetros diferentes. Vamos escrever essa função.
void* print_multiple_messages(void* ptr) { char* message = (char*) ptr; for(int i=0; i<1000; ++i) { printf("%s \n", message); } }
Como você pode ver, esta função não faz nada grande. Ele pega uma mensagem como parâmetro de entrada e a imprime mil vezes.
Vamos escrever a função principal agora.
int main() { // Continue writing from here }
Assim como os processos, os threads também possuem IDs que são usados para identificá-los exclusivamente. Crie duas variáveis para conter esses IDs.
pthread_t thread1, thread2;
Estaremos usando mensagens diferentes para cada tópico. Crie duas strings (matriz de caracteres) para conter mensagens diferentes.
char* message1 = "Thread 1"; char* message2 = "Thread 2";
O próximo passo é criar os dois threads. Estaremos usando o método pthread_create
para fazer isso.
pthread_create(&thread1, NULL, print_multiple_messages, (void*) message1); pthread_create(&thread2, NULL, print_multiple_messages, (void*) message2);
Isso iniciará dois novos tópicos. Vamos instruir nosso processo principal a esperar até que os dois threads tenham concluído seu trabalho.
pthread_join(thread1, NULL); pthread_join(thread2, NULL);
E é isso. Compile o código e execute-o. Você notará que as mensagens de dois tópicos serão misturadas. Isso mostra que eles estão sendo executados em paralelo.
Parabéns, você acabou de criar seu primeiro tópico.
Então, neste artigo, falamos sobre Threads e Processos. Esses são alguns dos recursos mais fascinantes do Linux, e dominá-los é crucial. Ele permite o desenvolvimento de software com reconhecimento de hardware e o uso eficiente dos recursos à nossa disposição.
Aqui, vamos tirar uma conclusão para este artigo. Fizemos um esforço para detalhar o suficiente para você começar, mas não é isso. Então continue aprendendo mais. Se você gostou do conteúdo, talvez queira comentar e/ou emocionar.
Divirta-se aprendendo!