Chociaż na to pytanie udzielono odpowiedzi, pozwól mi opublikować szczegółowy przepływ zdarzeń w jądrze Linuksa.
Jest to w całości skopiowane z postów w systemie Linux: Sygnały z systemu Linux - elementy wewnętrzne
na blogu „Linux posts” pod adresem sklinuxblog.blogspot.in.
Program Space User C. Program
Zacznijmy od napisania prostego programu C przestrzeni użytkownika sygnału:
#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;
};
Ten kod przypisuje nową procedurę obsługi sygnału SIGINT. SIGINT można wysłać do uruchomionego procesu za pomocą kombinacji klawiszy Ctrl+ C. Po naciśnięciu przycisku Ctrl+ Cdo zadania wysyłany jest sygnał asynchroniczny SIGINT. Jest to również równoważne z wysłaniem kill -INT <pid>
polecenia do innego terminala.
Jeśli zrobisz kill -l
(to mała litera L
, co oznacza „listę”), poznasz różne sygnały, które można wysłać do uruchomionego procesu.
[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
Do wysyłania określonych sygnałów można także użyć następującej kombinacji klawiszy:
- Ctrl+ C- wysyła SIGINT, która domyślna akcja kończy działanie aplikacji.
- Ctrl+ \ - wysyła SIGQUIT, którego domyślnym działaniem jest zakończenie rdzenia zrzutu aplikacji.
- Ctrl+ Z- wysyła SIGSTOP, który zawiesza program.
Jeśli skompilujesz i uruchomisz powyższy program C, otrzymasz następujące dane wyjściowe:
[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop
Nawet z Ctrl+ Club kill -2 <pid>
proces się nie zakończy. Zamiast tego wykona procedurę obsługi sygnału i powróci.
Jak sygnał jest wysyłany do procesu
Jeśli zobaczymy wewnętrzne sygnały wysyłane do procesu i ustawimy Jprobe z funkcją dump_stack na __send_signal
funkcję, zobaczymy następujący ślad wywołania:
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
Tak więc główna funkcja wymaga wysłania sygnału:
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.
Teraz wszystko jest skonfigurowane i niezbędne zmiany task_struct
w procesie.
Obsługa sygnału
Sygnał jest sprawdzany / obsługiwany przez proces, gdy powraca z wywołania systemowego lub gdy nastąpi powrót z przerwania. Powrót z wywołania systemowego jest obecny w pliku entry_64.S
.
Wywoływana jest funkcja int_signal, z entry_64.S
której wywołuje się funkcję do_notify_resume()
.
Sprawdźmy funkcję do_notify_resume()
. Ta funkcja sprawdza, czy mamy TIF_SIGPENDING
ustawioną flagę w 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;
Połączenia i sygnały systemowe
„Powolne” wywołania systemowe, np. Blokujące odczyt / zapis, wprowadzające procesy w stan oczekiwania:
TASK_INTERRUPTIBLE
lub TASK_UNINTERRUPTIBLE
.
Zadanie w stanie TASK_INTERRUPTIBLE
zostanie zmienione na TASK_RUNNING
stan za pomocą sygnału. TASK_RUNNING
oznacza, że można zaplanować proces.
Jeśli zostanie wykonany, jego procedura obsługi sygnału zostanie uruchomiona przed zakończeniem „powolnego” wywołania systemowego. syscall
Nie zakończy się domyślnie.
Jeśli SA_RESTART
ustawiona syscall
jest flaga, jest restartowana po zakończeniu procedury obsługi sygnału.
Referencje