Embora esta questão tenha sido respondida,
deixe-me publicar um fluxo detalhado de eventos no kernel do Linux.
Isso é copiado inteiramente de postagens do Linux: Linux Signals - Internals
no blog "posts do Linux" em sklinuxblog.blogspot.in.
Programa C do espaço do usuário de sinal
Vamos começar escrevendo um simples programa C de espaço de usuário de sinal:
#include<signal.h>
#include<stdio.h>
/* Handler function */
void handler(int sig) {
printf("Receive signal: %u\n", sig);
};
int main(void) {
struct sigaction sig_a;
/* Initialize the signal handler structure */
sig_a.sa_handler = handler;
sigemptyset(&sig_a.sa_mask);
sig_a.sa_flags = 0;
/* Assign a new handler function to the SIGINT signal */
sigaction(SIGINT, &sig_a, NULL);
/* Block and wait until a signal arrives */
while (1) {
sigsuspend(&sig_a.sa_mask);
printf("loop\n");
}
return 0;
};
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 a tecla Ctrl + C é pressionada, o sinal assíncrono SIGINT é enviado para a tarefa. Também é equivalente a enviar o comando kill -INT <pid>
em outro terminal.
Se você fizer um kill -l
(que é um minúsculo L
, que significa "lista"), você conhecerá os vários sinais que podem ser enviados para um processo em execução.
[root@linux ~]# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
A seguinte combinação de teclas também pode ser usada para enviar sinais específicos:
-
Ctrl + C - envia o SIGINT, cuja ação padrão é encerrar o aplicativo.
-
Ctrl + \ - envia o SIGQUIT, cuja ação padrão é terminar o núcleo de despejo do aplicativo.
-
Ctrl + Z - envia o SIGSTOP que suspende o programa.
Se você compilar e executar o programa C acima, você obterá a seguinte saída:
[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop
Mesmo com Ctrl + C ou kill -2 <pid>
, o processo não será finalizado. Em vez disso, ele executará o manipulador de sinal e retornará.
Como o sinal é enviado para o processo
Se nós vermos os internos do sinal enviando para um processo e colocarmos o Jprobe com dump_stack na função __send_signal
, nós veremos o seguinte rastreamento de chamada:
May 5 16:18:37 linux kernel: dump_stack+0x19/0x1b
May 5 16:18:37 linux kernel: my_handler+0x29/0x30 (probe)
May 5 16:18:37 linux kernel: complete_signal+0x205/0x250
May 5 16:18:37 linux kernel: __send_signal+0x194/0x4b0
May 5 16:18:37 linux kernel: send_signal+0x3e/0x80
May 5 16:18:37 linux kernel: do_send_sig_info+0x52/0xa0
May 5 16:18:37 linux kernel: group_send_sig_info+0x46/0x50
May 5 16:18:37 linux kernel: __kill_pgrp_info+0x4d/0x80
May 5 16:18:37 linux kernel: kill_pgrp+0x35/0x50
May 5 16:18:37 linux kernel: n_tty_receive_char+0x42b/0xe30
May 5 16:18:37 linux kernel: ? ftrace_ops_list_func+0x106/0x120
May 5 16:18:37 linux kernel: n_tty_receive_buf+0x1ac/0x470
May 5 16:18:37 linux kernel: flush_to_ldisc+0x109/0x160
May 5 16:18:37 linux kernel: process_one_work+0x17b/0x460
May 5 16:18:37 linux kernel: worker_thread+0x11b/0x400
May 5 16:18:37 linux kernel: rescuer_thread+0x400/0x400
May 5 16:18:37 linux kernel: kthread+0xcf/0xe0
May 5 16:18:37 linux kernel: kthread_create_on_node+0x140/0x140
May 5 16:18:37 linux kernel: ret_from_fork+0x7c/0xb0
May 5 16:18:37 linux kernel: ? kthread_create_on_node+0x140/0x140
Portanto, a principal função para enviar o sinal é como:
First shell send the Ctrl+C signal using n_tty_receive_char
n_tty_receive_char()
isig()
kill_pgrp()
__kill_pgrp_info()
group_send_sig_info() -- for each PID in group call this function
do_send_sig_info()
send_signal()
__send_signal() -- allocates a signal structure and add to task pending signals
complete_signal()
signal_wake_up()
signal_wake_up_state() -- sets TIF_SIGPENDING in the task_struct flags. Then it wake up the thread to which signal was delivered.
Agora tudo está configurado e as alterações necessárias são feitas na task_struct
do 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 de entry_64.S
, que chama a função do_notify_resume()
.
Vamos verificar a função do_notify_resume()
. Esta função verifica se temos o sinalizador TIF_SIGPENDING
definido no task_struct
:
/* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);
do_signal calls handle_signal to call the signal specific handler
Signals are actually run in user mode in function:
__setup_rt_frame -- this sets up the instruction pointer to handler: regs->ip = (unsigned long) ksig->ka.sa.sa_handler;
Chamadas e sinais do SISTEMA
syscalls "lentos", e. bloqueio de leitura / gravação, colocar processos em estado de espera:
TASK_INTERRUPTIBLE
ou TASK_UNINTERRUPTIBLE
.
Uma tarefa no estado TASK_INTERRUPTIBLE
será alterada para o estado TASK_RUNNING
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 SA_RESTART
sinalizador for definido, syscall
será reiniciado depois que o manipulador de sinal terminar.
Referências