Em geral, para matar processos que geram sinais como SIGKILL
, SIGTSTP
etc.
Mas como é sabido quem ordenou esse sinal específico, quem o enviou a um processo específico e, em geral, como os sinais executam suas operações? Como os sinais funcionam internamente?
kill
signals
architecture
Varun Chhangani
fonte
fonte
Respostas:
A visão de 50.000 pés é a seguinte:
Um sinal é gerado pelo kernel internamente (por exemplo,
SIGSEGV
quando um endereço inválido é acessado ouSIGQUIT
quando você pressiona Ctrl+ \), ou por um programa que usa okill
syscall (ou vários relacionados).Se for por um dos syscalls, o kernel confirmará que o processo de chamada possui privilégios suficientes para enviar o sinal. Caso contrário, um erro é retornado (e o sinal não acontece).
Se for um dos dois sinais especiais, o kernel age incondicionalmente, sem nenhuma entrada do processo de destino. Os dois sinais especiais são SIGKILL e SIGSTOP. Todas as informações abaixo sobre ações padrão, sinais de bloqueio etc. são irrelevantes para esses dois.
Em seguida, o kernel descobre o que fazer com o sinal:
Para cada processo, há uma ação associada a cada sinal. Existem vários padrões, e os programas podem definir diferentes usando
sigaction
,signal
etc. Isso inclui coisas como "ignorar completamente", "interromper o processo", "interromper o processo com um dump principal", "interromper o processo", etc.Os programas também podem desativar a entrega de sinais ("bloqueados"), sinal por sinal. Então o sinal permanece pendente até ser desbloqueado.
Os programas podem solicitar que, em vez de o kernel tomar alguma ação, ele entregue o sinal ao processo de forma síncrona (com
sigwait
, et al. Ousignalfd
) ou de forma assíncrona (interrompendo o que o processo está fazendo e chamando uma função especificada).Há um segundo conjunto de sinais chamado "sinais em tempo real", que não têm significado específico, e também permitem que vários sinais sejam colocados em fila (sinais normais enfileiram apenas um de cada quando o sinal está bloqueado). Eles são usados em programas multithread para que os threads se comuniquem. Vários são usados na implementação de threads POSIX da glibc, por exemplo. Eles também podem ser usados para se comunicar entre diferentes processos (por exemplo, você pode usar vários sinais em tempo real para que um programa fooctl envie uma mensagem ao foo daemon).
Para uma visão que não seja de 50.000 pés, tente
man 7 signal
e também a documentação interna (ou fonte) do kernel.fonte
A implementação do sinal é muito complexa e específica do kernel. Em outras palavras, diferentes kernels implementarão sinais de maneira diferente. Uma explicação simplificada é a seguinte:
A CPU, com base em um valor especial de registro, possui um endereço na memória onde espera encontrar uma "tabela de descritores de interrupção", que na verdade é uma tabela vetorial. Há um vetor para todas as exceções possíveis, como divisão por zero, ou interceptação, como INT 3 (depuração). Quando a CPU encontra a exceção, ela salva os sinalizadores e o ponteiro de instrução atual na pilha e depois salta para o endereço especificado pelo vetor relevante. No Linux, esse vetor sempre aponta para o kernel, onde há um manipulador de exceções. A CPU está pronta e o kernel Linux assume o controle.
Observe que você também pode acionar uma exceção do software. Por exemplo, o usuário pressiona CTRL- C, então essa chamada vai para o kernel que chama seu próprio manipulador de exceções. Em geral, existem diferentes maneiras de chegar ao manipulador, mas independentemente da mesma coisa básica acontece: o contexto é salvo na pilha e o manipulador de exceção do kernel é pulado para.
O manipulador de exceção decide qual thread deve receber o sinal. Se algo como divisão por zero ocorreu, é fácil: o encadeamento que causou a exceção recebe o sinal, mas para outros tipos de sinais, a decisão pode ser muito complexa e, em alguns casos incomuns, um encadeamento mais ou menos aleatório pode pegue o sinal.
Para enviar o sinal, o que o kernel faz é primeiro definir um valor indicando o tipo de sinal,
SIGHUP
ou o que seja. Este é apenas um número inteiro. Todo processo possui uma área de memória de "sinal pendente" onde esse valor é armazenado. Em seguida, o kernel cria uma estrutura de dados com as informações do sinal. Essa estrutura inclui um sinal de "disposição" que pode ser o padrão, ignorar ou manipular. O kernel então chama sua própria funçãodo_signal()
. A próxima fase começa.do_signal()
primeiro decide se ele irá lidar com o sinal. Por exemplo, se é uma morte ,do_signal()
apenas mata o processo, fim da história. Caso contrário, ele olha para a disposição. Se a disposição for padrão,do_signal()
lida com o sinal de acordo com uma política padrão que depende do sinal. Se a disposição for manipulada, significa que existe uma função no programa do usuário projetada para manipular o sinal em questão e o ponteiro para esta função estará na estrutura de dados acima mencionada. Nesse caso, do_signal () chama outra função do kernel,handle_signal()
, que passa pelo processo de retornar ao modo de usuário e chamar essa função. Os detalhes dessa transferência são extremamente complexos. Esse código no seu programa geralmente é vinculado automaticamente ao seu programa quando você usa as funções nosignal.h
.Examinando o valor do sinal pendente adequadamente, o kernel pode determinar se o processo está manipulando todos os sinais e tomará as ações apropriadas, caso contrário, o que pode estar colocando o processo em suspensão ou matando-o ou outra ação, dependendo do sinal.
fonte
Embora essa pergunta tenha sido respondida, deixe-me postar um fluxo detalhado de eventos no kernel do Linux.
Isso é copiado inteiramente das postagens do Linux: Linux Signals - Internals no blog “Linux posts” em sklinuxblog.blogspot.in.
Programa C do Espaço do Usuário de Sinal
Vamos começar escrevendo um programa C de espaço de usuário de sinal simples:
Este código atribui um novo manipulador para o sinal SIGINT. O SIGINT pode ser enviado para o processo em execução usando a combinação de teclas Ctrl+ C. Quando Ctrl+ Cé pressionado, o sinal assíncrono SIGINT é enviado para a tarefa. Também é equivalente a enviar o
kill -INT <pid>
comando em outro terminal.Se você fizer uma
kill -l
(que é minúsculaL
, que significa “lista”), conhecerá os vários sinais que podem ser enviados para um processo em execução.Também pode ser usada a seguinte combinação de teclas para enviar sinais específicos:
Se você compilar e executar o programa C acima, obterá a seguinte saída:
Mesmo com Ctrl+ Cou
kill -2 <pid>
o processo não será finalizado. Em vez disso, ele executará o manipulador de sinal e retornará.Como o sinal é enviado ao processo
Se virmos as partes internas do sinal enviando para um processo e colocarmos o Jprobe com dump_stack na
__send_signal
função, veremos o seguinte rastreamento de chamada:Portanto, a principal função solicita o envio do sinal:
Agora tudo está configurado e as alterações necessárias são feitas
task_struct
no processo.Manipulação de sinal
O sinal é verificado / tratado por um processo quando retorna da chamada do sistema ou se o retorno da interrupção é feito. O retorno da chamada do sistema está presente no arquivo
entry_64.S
.A função int_signal é chamada a partir da
entry_64.S
qual chama a funçãodo_notify_resume()
.Vamos verificar a função
do_notify_resume()
. Esta função verifica se temos oTIF_SIGPENDING
sinalizador definido notask_struct
:Chamadas e sinais do sistema
Chamadas "lentas", por exemplo, bloqueio de leitura / gravação, processos em estado de espera:
TASK_INTERRUPTIBLE
ouTASK_UNINTERRUPTIBLE
.Uma tarefa no estado
TASK_INTERRUPTIBLE
será alterada para oTASK_RUNNING
estado por um sinal.TASK_RUNNING
significa que um processo pode ser agendado.Se executado, seu manipulador de sinal será executado antes da conclusão do syscall "lento". O
syscall
não é concluído por padrão.Se o
SA_RESTART
sinalizador estiver definido,syscall
será reiniciado depois que o manipulador de sinal terminar.Referências
fonte
kill
comando, que é um shell embutido). (2c) Embora ponto-e-vírgula após o fechamento}
de uma função não sejam, estritamente falando, erros, eles são desnecessários e altamente heterodoxos. (3) Mesmo que tudo estivesse correto, não seria uma resposta muito boa para a pergunta. (3a) A questão, embora pouco clara, parece estar focada em como os atores (usuários e processos) iniciam (ou seja, enviam ) sinais. ... (continua)