Though this question has been answered,
let me post a detailed flow of events in Linux kernel.
This is copied entirely from Linux posts: Linux Signals - Internals
at the “Linux posts” blog at sklinuxblog.blogspot.com. These blogs are also written by me.
Signal User Space C Program
Let’s start with writing a simple signal user space C program:
#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;
};
This code assigns a new handler for SIGINT signal. SIGINT can be sent to the running process using Ctrl+C key combination. When Ctrl+C is pressed then the asynchronous signal SIGINT is sent to the task. It is also equivalent to sending the kill -INT <pid>
command in other terminal.
If you do a kill -l
(that’s a lowercase L
, which stands for “list”) you will come to know the various signals which can be sent to a running process.
[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
Also following key combination can be used to send particular signals:
- Ctrl+C – sends SIGINT which default action is to terminate the application.
- Ctrl+</kbd> – sends SIGQUIT which default action is to terminate the application dumping core.
- Ctrl+Z – sends SIGSTOP that suspends the program.
If you compile and run the above C program then you will get the following output:
[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop
Even with Ctrl+C or kill -2 <pid>
the process will not terminate. Instead it will execute the signal handler and return.
How the signal is sent to the process
If we see the internals of the signal sending to a process and put Jprobe with dump_stack at __send_signal
function we will see following call 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
So the major function calls for sending the signal goes like:
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.
Now everything is set up and necessary changes are done to the task_struct
of the process.
Handling of signal
The signal is checked/handled by a process when it returns from system call or if the return from interrupt is done. The return from the system call is present in file entry_64.S
.
The function int_signal function is called from entry_64.S
which calls the function do_notify_resume()
.
Let’s check the function do_notify_resume()
. This function checks if we have the TIF_SIGPENDING
flag set in the 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 calls and signals
“Slow” syscalls e.g. blocking read/write, put processes into waiting state:
TASK_INTERRUPTIBLE
or TASK_UNINTERRUPTIBLE
.
A task in state TASK_INTERRUPTIBLE
will be changed to the TASK_RUNNING
state by a signal. TASK_RUNNING
means a process can be scheduled.
If executed, its signal handler will be run before completion of “slow” syscall. The syscall
does not complete by default.
If SA_RESTART
flag set, syscall
is restarted after signal handler finishes.
References