Como o SIGINT se relaciona com os outros sinais de terminação, como SIGTERM, SIGQUIT e SIGKILL?

103

Em sistemas POSIX, os sinais de terminação geralmente têm a seguinte ordem (de acordo com muitas páginas MAN e as especificações POSIX):

  1. SIGTERM - peça educadamente que um processo seja encerrado. Ele deve terminar normalmente, limpando todos os recursos (arquivos, soquetes, processos filho, etc.), excluindo arquivos temporários e assim por diante.

  2. SIGQUIT - pedido mais forte. Ele deve terminar sem graça, ainda limpando recursos que absolutamente precisam de limpeza, mas talvez não exclua arquivos temporários, talvez escreva informações de depuração em algum lugar; em alguns sistemas também um dump de memória será gravado (independentemente se o sinal foi capturado pelo aplicativo ou não).

  3. SIGKILL - pedido mais forte. O processo nem é solicitado a fazer nada, mas o sistema irá limpar o processo, quer goste ou não. Provavelmente, um dump de memória foi gravado.

Como o SIGINT se encaixa nessa imagem? Um processo CLI geralmente é encerrado por SIGINT quando o usuário pressiona CRTL + C, no entanto, um processo em segundo plano também pode ser encerrado por SIGINT usando o utilitário KILL. O que não consigo ver nas especificações ou nos arquivos de cabeçalho é se o SIGINT é mais ou menos forte do que o SIGTERM ou se há alguma diferença entre o SIGINT e o SIGTERM.

ATUALIZAR:

A melhor descrição dos sinais de término que encontrei até agora está na documentação GNU LibC . Isso explica muito bem que existe uma diferença pretendida entre SIGTERM e SIGQUIT.

Diz sobre SIGTERM:

É a maneira normal de pedir educadamente que um programa seja encerrado.

E diz sobre SIGQUIT:

e produz um core dump ao encerrar o processo, como um sinal de erro de programa. Você pode pensar nisso como uma condição de erro do programa “detectada” pelo usuário. [...] Certos tipos de limpeza são melhor omitidos no manuseio do SIGQUIT. Por exemplo, se o programa criar arquivos temporários, ele deve lidar com as outras solicitações de encerramento excluindo os arquivos temporários. Mas é melhor para o SIGQUIT não excluí-los, para que o usuário possa examiná-los em conjunto com o dump principal.

E SIGHUP também é explicado bem o suficiente. SIGHUP não é realmente um sinal de término, significa apenas que a "conexão" com o usuário foi perdida, então o aplicativo não pode esperar que o usuário leia mais nenhuma saída (por exemplo, saída stdout / stderr) e não há nenhuma entrada a esperar do usuário por mais tempo. Para a maioria dos aplicativos, isso significa que é melhor encerrar. Em teoria, um aplicativo também pode decidir que entra no modo daemon quando um SIGHUP é recebido e agora é executado como um processo em segundo plano, gravando a saída em um arquivo de log configurado. Para a maioria dos daemons já em execução em segundo plano, SIGHUP normalmente significa que eles devem reexaminar seus arquivos de configuração, portanto, você os envia para processos de segundo plano após editar os arquivos de configuração.

No entanto, não há nenhuma explicação útil do SIGINT nesta página, a não ser que ele é enviado por CRTL + C. Existe alguma razão pela qual alguém lidaria com o SIGINT de uma maneira diferente do SIGTERM? Em caso afirmativo, qual seria o motivo e como o tratamento seria diferente?

Mecki
fonte
1
Boa pergunta. Suspeito que parte do cruft histórico do Unix estaria envolvido na resposta.
Alex B
Sei que essa pergunta é antiga, mas caso alguém tenha vindo aqui para obter uma lista extensa de sinais para seu sistema operacional (Linux), eles podem ser encontrados digitando sudo fuser -lem seu prompt de comando. Para mim, isso traz à tona:HUP INT QUIT ILL TRAP ABRT IOT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH IO PWR SYS UNUSED
Nick Bull
3
Experimente também kill -luma lista de sinais.
AAAfarmclub

Respostas:

88

SIGTERM e SIGKILL destinam-se a solicitações de finalidades gerais de "encerramento deste processo". SIGTERM (por padrão) e SIGKILL (sempre) causarão o encerramento do processo. SIGTERM pode ser capturado pelo processo (por exemplo, para que ele possa fazer sua própria limpeza se quiser), ou até mesmo ser completamente ignorado; mas SIGKILL não pode ser capturado ou ignorado.

SIGINT e SIGQUIT destinam-se especificamente a solicitações do terminal: caracteres de entrada particulares podem ser atribuídos para gerar esses sinais (dependendo das configurações de controle do terminal). A ação padrão para SIGINT é o mesmo tipo de término de processo que a ação padrão para SIGTERM e a ação inalterável para SIGKILL; a ação padrão para SIGQUIT também é o término do processo, mas ações adicionais definidas pela implementação podem ocorrer, como a geração de um dump de memória. Ambos podem ser capturados ou ignorados pelo processo, se necessário.

SIGHUP, como você disse, destina-se a indicar que a conexão do terminal foi perdida, ao invés de ser um sinal de terminação como tal. Mas, novamente, a ação padrão para SIGHUP (se o processo não o capturar ou ignorar) é encerrar o processo da mesma forma que SIGTERM etc.

Há uma tabela nas definições POSIX para a signal.hqual lista os vários sinais e suas ações e propósitos padrão, e o capítulo Interface geral do terminal inclui muito mais detalhes sobre os sinais relacionados ao terminal.

Matthew Slattery
fonte
10
Este é o ponto chave: SIGINT e SIGQUIT podem ser gerados a partir do terminal usando caracteres únicos, enquanto o programa está sendo executado. Os outros sinais devem ser gerados por outro programa, de alguma forma (por exemplo, pelo comando kill). SIGINT é menos violento que SIGQUIT; o último produz um core dump. SIGKILL não pode ser capturado. SIGHUP é gerado se sua conexão desligar (a janela fecha, etc). Então, todos eles têm significados diferentes.
Jonathan Leffler
TTY Demystified tem algumas informações de fácil digestão sobre como os sinais interagem com o subsistema tty do kernel. Adiciona algum contexto à sua resposta.
Daniel Näslund
1
Parece que as especificações não são muito precisas, mas meu entendimento é que SIgint pede ao programa para interromper sua ação atual, não necessariamente encerrar. Para um programa projetado para fazer apenas uma ação por execução que implica desistência, mas para um programa interativo que tem algum tipo de loop de leitura-avaliação-impressão, pode significar desistir da avaliação atual e voltar a ler a entrada do usuário. Acho que menos faz isso em sigint.
bdsl
Qual sinal é enviado durante a reinicialização?
Dan Dascalescu
10

Como DarkDust observou, muitos sinais têm os mesmos resultados, mas os processos podem anexar diferentes ações a eles, distinguindo como cada sinal é gerado. Olhando para o código-fonte do kernel do FreeBSD (kern_sig.c), vejo que os dois sinais são tratados da mesma maneira, eles encerram o processo e são entregues a qualquer thread.

SA_KILL|SA_PROC,             /* SIGINT */
SA_KILL|SA_PROC,             /* SIGTERM */
Diomidis Spinellis
fonte
9

man 7 signal

Esta é a página de manual não normativa conveniente do projeto de páginas de manual do Linux que você frequentemente deseja consultar para obter informações de sinal do Linux.

A versão 3.22 menciona coisas interessantes como:

Os sinais SIGKILL e SIGSTOP não podem ser capturados, bloqueados ou ignorados.

e contém a tabela:

Signal     Value     Action   Comment
----------------------------------------------------------------------
SIGHUP        1       Term    Hangup detected on controlling terminal
                              or death of controlling process
SIGINT        2       Term    Interrupt from keyboard
SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal
SIGSEGV      11       Core    Invalid memory reference
SIGPIPE      13       Term    Broken pipe: write to pipe with no
                              readers
SIGALRM      14       Term    Timer signal from alarm(2)
SIGTERM      15       Term    Termination signal
SIGUSR1   30,10,16    Term    User-defined signal 1
SIGUSR2   31,12,17    Term    User-defined signal 2
SIGCHLD   20,17,18    Ign     Child stopped or terminated
SIGCONT   19,18,25    Cont    Continue if stopped
SIGSTOP   17,19,23    Stop    Stop process
SIGTSTP   18,20,24    Stop    Stop typed at tty
SIGTTIN   21,21,26    Stop    tty input for background process
SIGTTOU   22,22,27    Stop    tty output for background process

que resume o sinal Actionque distingue, por exemplo, SIGQUIT de SIGQUIT, uma vez que SIGQUIT tem ação Coree SIGINT Term.

As ações são documentadas no mesmo documento:

The entries in the "Action" column of the tables below specify the default disposition for each signal, as follows:

Term   Default action is to terminate the process.

Ign    Default action is to ignore the signal.
Core   Default action is to terminate the process and dump core (see core(5)).
Stop   Default action is to stop the process.
Cont   Default action is to continue the process if it is currently stopped.

Não consigo ver nenhuma diferença entre SIGTERM e SIGINT do ponto de vista do kernel, pois ambos têm ação Terme podem ser capturados. Parece que é apenas uma "distinção de convenção de uso comum":

  • SIGINT é o que acontece quando você faz CTRL-C no terminal
  • SIGTERM é o sinal padrão enviado por kill

Alguns sinais são ANSI C e outros não

Uma diferença considerável é que:

  • SIGINT e SIGTERM são ANSI C, portanto, mais portáteis
  • SIGQUIT e SIGKILL não são

Eles são descritos na seção "7.14 Manuseio de sinais" do projeto C99 N1256 :

  • Recebimento SIGINT de um sinal de atenção interativo
  • SIGTERM um pedido de rescisão enviado ao programa

o que torna o SIGINT um bom candidato para um Ctrl + C interativo

POSIX 7

POSIX 7 documenta os sinais com o signal.hcabeçalho: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html

Esta página também tem a seguinte tabela de interesse que menciona algumas das coisas que já tínhamos visto em man 7 signal:

Signal    Default Action   Description
SIGABRT   A                Process abort signal.
SIGALRM   T                Alarm clock.
SIGBUS    A                Access to an undefined portion of a memory object.
SIGCHLD   I                Child process terminated, stopped,
SIGCONT   C                Continue executing, if stopped.
SIGFPE    A                Erroneous arithmetic operation.
SIGHUP    T                Hangup.
SIGILL    A                Illegal instruction.
SIGINT    T                Terminal interrupt signal.
SIGKILL   T                Kill (cannot be caught or ignored).
SIGPIPE   T                Write on a pipe with no one to read it.
SIGQUIT   A                Terminal quit signal.
SIGSEGV   A                Invalid memory reference.
SIGSTOP   S                Stop executing (cannot be caught or ignored).
SIGTERM   T                Termination signal.
SIGTSTP   S                Terminal stop signal.
SIGTTIN   S                Background process attempting read.
SIGTTOU   S                Background process attempting write.
SIGUSR1   T                User-defined signal 1.
SIGUSR2   T                User-defined signal 2.
SIGTRAP   A                Trace/breakpoint trap.
SIGURG    I                High bandwidth data is available at a socket.
SIGXCPU   A                CPU time limit exceeded.
SIGXFSZ   A                File size limit exceeded.

BusyBox init

O rebootcomando padrão 1.29.2 do BusyBox envia um SIGTERM para os processos, dorme por um segundo e, em seguida, envia o SIGKILL. Esta parece ser uma convenção comum em diferentes distros.

Quando você desliga um sistema BusyBox com:

reboot

ele envia um sinal para o processo de inicialização.

Então, o manipulador de sinal init termina chamando:

static void run_shutdown_and_kill_processes(void)
{
    /* Run everything to be run at "shutdown".  This is done _prior_
     * to killing everything, in case people wish to use scripts to
     * shut things down gracefully... */
    run_actions(SHUTDOWN);

    message(L_CONSOLE | L_LOG, "The system is going down NOW!");

    /* Send signals to every process _except_ pid 1 */
    kill(-1, SIGTERM);
    message(L_CONSOLE, "Sent SIG%s to all processes", "TERM");
    sync();
    sleep(1);

    kill(-1, SIGKILL);
    message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
    sync();
    /*sleep(1); - callers take care about making a pause */
}

que imprime no terminal:

The system is going down NOW!
Sent SIGTERM to all processes
Sent SIGKILL to all processes

Aqui está um exemplo concreto mínimo disso .

Sinais enviados pelo kernel

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
8

Após uma rápida pesquisa no Google por sigint vs sigterm , parece que a única diferença pretendida entre os dois é se foi iniciada por um atalho de teclado ou por uma chamada explícita para kill.

Como resultado, você pode, por exemplo, interceptar sigint e fazer algo especial com ele, sabendo que provavelmente foi enviado por um atalho de teclado. Talvez atualize a tela ou algo assim, em vez de morrer (não recomendado, pois as pessoas esperam ^Cmatar o programa, apenas um exemplo).

Também aprendi que ^\deve enviar sigquit, que posso começar a usar sozinho. Parece muito útil.

Jonathan
fonte
3

Usando kill(tanto a chamada do sistema quanto o utilitário`, você pode enviar quase qualquer sinal para qualquer processo, desde que tenha permissão. Um processo não consegue distinguir como um sinal ganhou vida e quem o enviou.

Dito isso, SIGINT realmente foi feito para marcar a interrupção Ctrl-C, enquanto SIGTERM é o sinal de terminal geral. Não existe o conceito de um sinal ser "mais forte", com a única exceção de que existem sinais que não podem ser bloqueados ou manipulados (SIGKILL e SIGSTOP, de acordo com a página de manual).

Um sinal só pode ser "mais forte" do que outro sinal no que diz respeito a como um processo de recebimento trata o sinal (e qual é a ação padrão para esse sinal). Por exemplo, por padrão, SIGTERM e SIGINT levam ao encerramento. Mas se você ignorar o SIGTERM, ele não encerrará o processo, enquanto o SIGINT ainda o faz.

DarkDust
fonte
0

Com exceção de alguns sinais, os manipuladores de sinal podem capturar os vários sinais, ou o comportamento padrão ao receber um sinal pode ser modificado. Veja a signal(7)página do manual para detalhes.

Ignacio Vazquez-Abrams
fonte
Sim, mas se não sei "o significado" de um sinal, não adianta captar o sinal, pois não saberei o que fazer no meu aplicativo. GNU C, por exemplo, explica a diferença entre SIGQUIT e SIGTERM. Mas dá poucas informações sobre SIGINT: gnu.org/s/libc/manual/html_node/Termination-Signals.html
Mecki
"O SIGINTsinal (“ interrupção do programa ”) é enviado quando o usuário digita o caractere INTR (normalmente C-c)." "SIGINT 2 Term Interrupt do teclado" Você pressiona Ctrl-C, SIGINTé enviado. O processo normalmente morre. O que mais para dar?
Ignacio Vazquez-Abrams
Potencialmente, pode haver uma descrição da intenção que o programador deve presumir que o usuário terá quando pressionar ctrl-c, isto é, a semântica da mensagem. Como exemplo, a especificação HTTP deixa claro que quando um usuário envia uma solicitação GET, o servidor não deve presumir que deseja que ele faça qualquer coisa, exceto enviar uma resposta.
bdsl