Obwohl diese Frage beantwortet wurde, möchte ich einen detaillierten Ablauf von Ereignissen im Linux-Kernel veröffentlichen.
Dies wurde vollständig von Linux-Posts kopiert : Linux-Signale -
Interna im Blog „Linux posts“ unter sklinuxblog.blogspot.in.
Signal User Space C-Programm
Beginnen wir mit dem Schreiben eines einfachen C-Programms für den Signalbenutzerraum:
#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;
};
Dieser Code weist einen neuen Handler für das SIGINT-Signal zu. SIGINT kann mit der Tastenkombination Ctrl+ an den laufenden Prozess gesendet werden C. Wenn Ctrl+ Cgedrückt wird, wird das asynchrone Signal SIGINT an die Task gesendet. Dies entspricht auch dem Senden des kill -INT <pid>
Befehls in einem anderen Terminal.
Wenn Sie eine kill -l
(das ist ein Kleinbuchstabe L
, der für "Liste" steht) ausführen, werden Sie die verschiedenen Signale kennen, die an einen laufenden Prozess gesendet werden können.
[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
Folgende Tastenkombination kann auch zum Senden bestimmter Signale verwendet werden:
- Ctrl+ C- Sendet SIGINT, dessen Standardaktion darin besteht, die Anwendung zu beenden.
- Ctrl+ \ - Sendet SIGQUIT, dessen Standardaktion darin besteht, den Anwendungs-Dump-Core zu beenden.
- Ctrl+ Z- Sendet SIGSTOP, das das Programm anhält.
Wenn Sie das obige C-Programm kompilieren und ausführen, erhalten Sie die folgende Ausgabe:
[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop
Auch mit Ctrl+ Coder wird kill -2 <pid>
der Vorgang nicht beendet. Stattdessen führt es den Signalhandler aus und kehrt zurück.
Wie das Signal an den Prozess gesendet wird
Wenn wir die Interna des Signals sehen, das an einen Prozess gesendet wird, und Jprobe mit dump_stack auf __send_signal
function setzen, sehen wir folgenden Aufruf-Trace:
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
Die Hauptfunktion zum Senden des Signals lautet also:
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.
Jetzt ist alles eingerichtet und die notwendigen Änderungen werden task_struct
am Prozess vorgenommen.
Umgang mit Signal
Das Signal wird von einem Prozess geprüft / verarbeitet, wenn es vom Systemaufruf zurückkommt oder wenn die Rückkehr vom Interrupt erfolgt ist. Die Rückkehr vom Systemaufruf ist in Datei vorhanden entry_64.S
.
Es wird die Funktion int_signal aufgerufen, von entry_64.S
der aus die Funktion aufgerufen wird do_notify_resume()
.
Lassen Sie uns die Funktion überprüfen do_notify_resume()
. Diese Funktion prüft, ob wir das TIF_SIGPENDING
Flag gesetzt haben in 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;
SYSTEM ruft und signalisiert
"Langsame" Systemaufrufe, z. B. Blockieren von Lese- / Schreibvorgängen, Versetzen von Prozessen in den Wartezustand:
TASK_INTERRUPTIBLE
oder TASK_UNINTERRUPTIBLE
.
Ein Task im Status TASK_INTERRUPTIBLE
wird TASK_RUNNING
durch ein Signal in den Status geändert . TASK_RUNNING
bedeutet, dass ein Prozess geplant werden kann.
Wenn es ausgeführt wird, wird sein Signalhandler ausgeführt, bevor der langsame Systemaufruf abgeschlossen ist. Der syscall
wird standardmäßig nicht abgeschlossen.
Wenn das SA_RESTART
Flag gesetzt ist, syscall
wird es nach Abschluss des Signalhandlers neu gestartet.
Verweise