Wednesday 6 May 2015

Linux Signals - Internals

Signal User Space C Program 

Lets 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 -2 <pid> command in other terminal.

If you do a kill -l 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-\ - 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 O/P
[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 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: [<ffffffff815e19ba>] dump_stack+0x19/0x1b
May  5 16:18:37 linux kernel: [<ffffffffa08e3029>] my_handler+0x29/0x30 [probe]
May  5 16:18:37 linux kernel: [<ffffffff81071a75>] complete_signal+0x205/0x250
May  5 16:18:37 linux kernel: [<ffffffff81071f54>] __send_signal+0x194/0x4b0
May  5 16:18:37 linux kernel: [<ffffffff810722ae>] send_signal+0x3e/0x80
May  5 16:18:37 linux kernel: [<ffffffff81072db2>] do_send_sig_info+0x52/0xa0
May  5 16:18:37 linux kernel: [<ffffffff81073326>] group_send_sig_info+0x46/0x50
May  5 16:18:37 linux kernel: [<ffffffff8107337d>] __kill_pgrp_info+0x4d/0x80
May  5 16:18:37 linux kernel: [<ffffffff810733e5>] kill_pgrp+0x35/0x50
May  5 16:18:37 linux kernel: [<ffffffff81374e8b>] n_tty_receive_char+0x42b/0xe30
May  5 16:18:37 linux kernel: [<ffffffff81106d46>] ? ftrace_ops_list_func+0x106/0x120
May  5 16:18:37 linux kernel: [<ffffffff81375a3c>] n_tty_receive_buf+0x1ac/0x470
May  5 16:18:37 linux kernel: [<ffffffff81378dc9>] flush_to_ldisc+0x109/0x160
May  5 16:18:37 linux kernel: [<ffffffff8107e02b>] process_one_work+0x17b/0x460
May  5 16:18:37 linux kernel: [<ffffffff8107edfb>] worker_thread+0x11b/0x400
May  5 16:18:37 linux kernel: [<ffffffff8107ece0>] ? rescuer_thread+0x400/0x400
May  5 16:18:37 linux kernel: [<ffffffff81085aef>] kthread+0xcf/0xe0
May  5 16:18:37 linux kernel: [<ffffffff81085a20>] ? kthread_create_on_node+0x140/0x140
May  5 16:18:37 linux kernel: [<ffffffff815f206c>] ret_from_fork+0x7c/0xb0
May  5 16:18:37 linux kernel: [<ffffffff81085a20>] ? 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()

Lets 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.

No comments:

Post a Comment