Como o Linux “mata” um processo?

91

Muitas vezes me surpreende que, embora eu tenha trabalhado profissionalmente com computadores por várias décadas e com o Linux por uma década, eu realmente trato a maior parte da funcionalidade do SO como uma caixa preta, não muito diferente da mágica.

Hoje eu pensei sobre o comando kill e, embora eu o use várias vezes por dia (tanto no sabor "normal" como no -9 ), devo admitir que não tenho absolutamente nenhuma ideia de como ele funciona nos bastidores .

Do meu ponto de vista, se um processo em execução for "interrompido", eu chamo kill em seu PID e, de repente, ele não está mais sendo executado. Magia!

O que realmente acontece lá? Man pages fala sobre "sinais", mas certamente isso é apenas uma abstração. Enviar kill -9 para um processo não requer a cooperação do processo (como manipular um sinal), apenas o mata.

  • Como o Linux impede que o processo continue a ocupar o tempo de CPU?
  • É removido do agendamento?
  • Ele desconecta o processo de seus identificadores de arquivos abertos?
  • Como a memória virtual do processo é liberada?
  • Existe algo como uma tabela global na memória, onde o Linux mantém referências a todos os recursos utilizados por um processo, e quando eu "matar" um processo, o Linux simplesmente passa por essa tabela e libera os recursos um por um?

Eu realmente gostaria de saber tudo isso!

    
por seratoninant 30.01.2014 / 10:19

3 respostas

71

Sending kill -9 to a process doesn't require the process' cooperation (like handling a signal), it just kills it off.

Você está presumindo que, porque alguns sinais podem ser capturados e ignorados, todos envolvem cooperação. Mas, como por man 2 signal , "os sinais SIGKILL e SIGSTOP não podem ser capturados ou ignorados". SIGTERM pode ser detectado, e é por isso que kill não é sempre efetivo - geralmente isso significa que alguma coisa no manipulador do processo não deu certo. 1

Se um processo não define (ou não pode) definir um manipulador para um dado sinal, o kernel executa uma ação padrão. No caso de SIGTERM e SIGKILL, isso é terminar o process (a menos que seu pid seja 1; o kernel não terminará init ) 2 significando que seus filehandles são fechados, sua memória retornada ao sistema pool, seu pai recebe SIGCHILD, seus filhos órfãos são herdados por init, etc, como se tivesse chamado exit (veja man 2 exit ). O processo não existe mais - a menos que acabe como um zumbi, caso em que ainda está listado na tabela de processos do kernel com alguma informação; isso acontece quando seu pai não wait e lida com essa informação corretamente. No entanto, os processos zumbis não têm mais nenhuma memória alocada para eles e, portanto, não podem continuar sendo executados.

Is there something like a global table in memory where Linux keeps references to all resources taken up by a process and when I "kill" a process Linux simply goes through that table and frees the resources one by one?

Eu acho que isso é preciso o suficiente. A memória física é rastreada por página (uma página geralmente igual a um bloco de 4 KB) e essas páginas são obtidas e retornadas para um pool global. É um pouco mais complicado, pois algumas páginas gratuitas são armazenadas em cache, caso os dados contidos sejam novamente necessários (isto é, dados que foram lidos de um arquivo ainda existente).

Manpages talk about "signals" but surely that's just an abstraction.

Claro, todos os sinais são uma abstração. Eles são conceituais, assim como "processos". Eu estou jogando semântica um pouco, mas se você quer dizer que SIGKILL é qualitativamente diferente de SIGTERM, então sim e não. Sim, no sentido de que não pode ser capturado, mas não no sentido de que ambos são sinais. Por analogia, uma maçã não é uma laranja, mas maçãs e laranjas são, segundo uma definição preconcebida, ambas frutas. SIGKILL parece mais abstrato já que você não pode pegá-lo, mas ainda é um sinal. Aqui está um exemplo de tratamento SIGTERM, tenho certeza que você já viu isso antes:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Recieved %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}             

Esse processo vai durar para sempre. Você pode executá-lo em um terminal e enviá-lo SIGTERM com kill . Ele cospe coisas como:

Recieved 15 from pid 25331, uid 1066.

1066 é meu uid. O pid será o do shell do qual kill é executado, ou o pid de kill se você for fork ( kill 25309 & echo $? ).

Novamente, não faz sentido configurar um manipulador para o SIGKILL porque ele não funcionará. 3 Se eu kill -9 25309 o processo for finalizado. Mas isso ainda é um sinal; o kernel tem a informação sobre quem enviou o sinal , que tipo de sinal ele é, etc.

1. Se você não viu a lista de possíveis sinais , consulte kill -l .

2. Outra exceção, como Tim Post menciona abaixo, aplica-se a processos em suspensão ininterrupta . Estes não podem ser acordados até que o problema subjacente seja resolvido e, portanto, TODOS os sinais (incluindo o SIGKILL) sejam adiados por toda a duração. Um processo não pode criar essa situação de propósito, no entanto.

3. Isso não significa usar kill -9 é uma coisa melhor para fazer na prática. Meu manipulador de exemplo é ruim, no sentido de que não leva a exit() . O propósito real de um manipulador SIGTERM é dar ao processo uma chance de fazer coisas como limpar arquivos temporários e, depois, sair voluntariamente. Se você usar kill -9 , ele não terá essa chance, portanto, faça isso apenas se a parte "sair voluntariamente" tiver falhado.

    
por 30.01.2014 / 11:34
3

Cada processo é executado para o horário agendado e, em seguida, é interrompido pelo cronômetro de hardware, para dar seu núcleo de CPU para outras tarefas. É por isso que é possível ter muito mais processos do que núcleos de CPU, ou mesmo rodar todo o sistema operacional com muitos processos em uma única CPU.

Após o processo ser interrompido, o controle retorna ao código do kernel. Esse código pode então tomar a decisão de não retomar a execução do processo interrompido, sem qualquer cooperação do lado do processo. kill -9 pode acabar em execução em qualquer linha do seu programa.

    
por 31.01.2014 / 09:12
0

Aqui está uma descrição idealizada de como o processo de matar funciona. Na prática, qualquer variante do Unix terá muitas complicações e otimizações adicionais.

O kernel tem uma estrutura de dados para cada processo que armazena informações sobre a memória que está mapeando, quais threads tem e quando são agendadas, quais arquivos foram abertos, etc. Se o kernel decide matar um processo, ele faz uma nota na estrutura de dados do processo (e talvez na estrutura de dados de cada um dos encadeamentos) de que o processo deve ser eliminado.

Se um dos encadeamentos do processo estiver atualmente agendado em outra CPU, o kernel pode disparar uma interrupção naquela outra CPU para fazer com que o encadeamento pare de executar mais rapidamente.

Quando o agendador percebe que um thread está em um processo que deve ser eliminado, ele não agendará mais.

Quando nenhum dos threads do processo estiver agendado mais, o kernel começa a liberar os recursos do processo (memória, descritores de arquivos,…). Cada vez que o kernel libera um recurso, ele verifica se o proprietário ainda possui recursos ativos. Uma vez que o processo não tenha mais recursos ativos (mapeamento de memória, descritor de arquivo aberto,…), a estrutura de dados para o processo em si pode ser liberada e a entrada correspondente pode ser removida da tabela de processos.

Alguns recursos podem ser liberados imediatamente (por exemplo, desalocando a memória que não está sendo usada por uma operação de E / S). Outros recursos devem aguardar, por exemplo, dados que descrevem uma operação de E / S não podem ser liberados enquanto a operação de E / S estiver em andamento (enquanto um DMA em andamento, a memória que está acessando está em uso, e o cancelamento do DMA requer o contato com o periférico. O driver para tal recurso é notificado e pode tentar acelerar o cancelamento; Quando a operação não estiver mais em andamento, o driver concluirá a liberação desse recurso.

(A entrada na tabela de processos é, na verdade, um recurso que pertence ao processo pai, que é liberado quando o processo é executado e o pai reconhece o evento .

    
por 11.05.2017 / 01:07