Хотя на этот вопрос дан ответ, позвольте мне опубликовать подробный поток событий в ядре Linux.
Это полностью скопировано из постов Linux: Linux Signals - Internals
в блоге «Посты Linux» на sklinuxblog.blogspot.in.
Программа "Пространство пользователя сигнала C"
Давайте начнем с написания простой программы для пользователя на языке C:
#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;
};
Этот код назначает новый обработчик для сигнала SIGINT. SIGINT может быть отправлен в рабочий процесс с помощью комбинации клавиш Ctrl+ C. При нажатии Ctrl+ Cна задание отправляется асинхронный сигнал SIGINT. Это также эквивалентно отправке kill -INT <pid>
команды в другой терминал.
Если вы сделаете kill -l
(это строчная буква L
, что означает «список»), вы узнаете различные сигналы, которые могут быть отправлены в работающий процесс.
[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
Также следующая комбинация клавиш может использоваться для отправки определенных сигналов:
- Ctrl+ C- отправляет SIGINT действие по умолчанию для завершения приложения.
- Ctrl+ \ - отправляет SIGQUIT, какое действие по умолчанию завершает работу ядра дампа приложения.
- Ctrl+ Z- отправляет SIGSTOP, который приостанавливает работу программы.
Если вы скомпилируете и запустите вышеуказанную программу на C, вы получите следующий вывод:
[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop
Даже с Ctrl+ Cили kill -2 <pid>
процесс не закончится. Вместо этого он выполнит обработчик сигнала и вернется.
Как сигнал отправляется в процесс
Если мы увидим внутреннюю часть сигнала, отправляемого процессу, и поместим Jprobe с dump_stack в __send_signal
функцию, то увидим следующую трассировку вызова:
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
Таким образом, основные вызовы функций для отправки сигнала выглядят так:
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.
Теперь все настроено и необходимые изменения внесены task_struct
в процесс.
Обработка сигнала
Сигнал проверяется / обрабатывается процессом, когда он возвращается из системного вызова или если выполняется возврат из прерывания. Возврат из системного вызова присутствует в файле entry_64.S
.
Функция int_signal вызывается функцией, из entry_64.S
которой вызывается функция do_notify_resume()
.
Давайте проверим функцию do_notify_resume()
. Эта функция проверяет, установлен ли у нас TIF_SIGPENDING
флаг в 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;
СИСТЕМНЫЕ звонки и сигналы
«Медленные» системные вызовы, например, блокировка чтения / записи, перевод процессов в состояние ожидания:
TASK_INTERRUPTIBLE
или TASK_UNINTERRUPTIBLE
.
Задание в состоянии TASK_INTERRUPTIBLE
будет изменено на TASK_RUNNING
состояние по сигналу. TASK_RUNNING
означает, что процесс может быть запланирован.
В случае выполнения его обработчик сигнала будет запущен до завершения «медленного» системного вызова. По syscall
умолчанию не завершается.
Если SA_RESTART
флаг установлен, syscall
перезапускается после завершения обработчика сигнала.
Ссылки