Where is the code for default signal handler in ELF binary? - c

I know that when we hit Ctrl+C, a SIGINT signal will be raised and the default action of terminating the process will be done by kernel. But where is the code for this termination coming from? Is it in the ELF binary or the kernel does it for us? I presume it is in the kernel and that is why we need custom handlers in our source code to override the signal behavior.
Any pointers will be much appreciated.

It is something kernel is doing for us. You can find all the information by reading signal.c file in kernel sources.
The point where kernel is trying to find a registered signal handler starts here: http://lxr.free-electrons.com/source/kernel/signal.c#L2257
2257 ka = &sighand->action[signr-1];
2258
2259 /* Trace actually delivered signals. */
2260 trace_signal_deliver(signr, &ksig->info, ka);
2261
2262 if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
2263 continue;
2264 if (ka->sa.sa_handler != SIG_DFL) {
2265 /* Run the handler. */
2266 ksig->ka = *ka;
2267
2268 if (ka->sa.sa_flags & SA_ONESHOT)
2269 ka->sa.sa_handler = SIG_DFL;
2270
2271 break; /* will return non-zero "signr" value */
2272 }
So, if there's a signal handler and if it is not "ignore signal" (SIG_IGN) and if it is not "default" handler (SIG_DEF), kernel will simply mark it for being run (and depending if it's one-shot it will move handler to default handler again).
However, if there's no signal handler registered, or if it is SIG_DEF, kernel checks if maybe it needs to pause the process, and finally kernel states the following:
2330 /*
2331 * Anything else is fatal, maybe with a core dump.
2332 */
http://lxr.free-electrons.com/source/kernel/signal.c#L2330

Let's say you kill(theShell, SIGINT). What happens is something like... (not showing kernel code because it's not actually relevant)
The C runtime library will take all the arguments to the system call sys_kill(), and proceed to execute the assembly code that performs a raw system call.
The kernel receives arguments, performs permissions checks, etc, etc...
If the process has the corresponding signal handler set to SIG_DEF, the kernel performs the corresponding default action directly and returns. If the process has the corresponding signal handler set to SIG_IGN, the signal is ignored and the system call returns. Otherwise, continue.
The signal is put on the signal queue for the target process, along with some information such as the sender.
Once a thread in the target process is selectable for receiving the signal, and doesn't have it masked out, the thread's context (CPU registers, stack pointer, etc...) is saved and the signal handler is invoked. If the thread was in a system call at the time of the signal's arrival, the system call returns -EINTR (for simplicity purposes) and the handler is invoked. Once the handler returns, the system call sys_sigreturn is automatically invoked, restoring the thread's state before the signal.
Meanwhile step 5 happens, the kill()ing process's system call returns.

Related

What's the earliest I can register a signal handler?

I have a program in C. I wish for it to always exit cleanly with exit code of 0 when it gets a SIGTERM. What is the earliest place I can register the signal handler? I added it at the top of my main(), but I worry it might get a sigterm just before the signal registers.
Is it possible to register a signal handler even earlier?
Yes you can. Using platform specific initializers such as gcc's __attribute((constructor)). But that's hardly a robust solution.
If you wish to "to always exit cleanly with exit code of 0 when it gets a SIGTERM", then instruct the process-spawning code to start with SIGTERM blocked.
Your main can then register a signal handler and unblock SIGTERM (with sigprocmask or pthread_sigmask, at which point the signal handler will run immediately if it had been received at any point in between process creation up to the signal-unblocking call.
Essentially, it will defer the delivery of the signal up to a point where you're ready too handle it.
(Note that if you start the process with the signal ignored rather than blocked, then any instance of the signal received up to unignoring the signal will have been lost, as if they never happened. That would seem to go against your stated requirement.)
If you can switch to C++: between start of the program and main global variables are initialized. So in theory you could have code like the following that would be run before main is called.
int f() {
signal(...);
return 0;
}
int x = f();
But you don't have a guarantee in which order global objects are initialized, so x might not be initialized first, but last.
But coming back to your original request: the time between starting the program and main is so short, why do you want to prepare against someone sending a SIGTERM in that short time? Isn't that too unlikely to happen?
If it is possible you could change the parent to ignore SIGTERM and then fork and execve. signal man page says
A child created via fork(2) inherits a copy of its parent's
signal dispositions. During an execve(2), the dispositions of
handled signals are reset to the default; the dispositions of
ignored signals are left unchanged.
So you could start your process ignoring SIGTERM until it sets a handler for SIGTERM.

Making yield function with timer in C

I want to write a code to switch between threads every 10 microseconds.
But the problem is in the yield function. I get an interrupt while running the timer handler. So it doesn't finish properly.
This is the code I have for initializing the timer:
signal(SIGALRM, &time_handler);
struct itimerval t1;
t1.it_interval.tv_sec = INTERVAL_SEC;
t1.it_interval.tv_usec = INTERVAL_USEC;
t1.it_value.tv_sec = INTERVAL_SEC;
t1.it_value.tv_usec = INTERVAL_USEC;
setitimer(ITIMER_REAL, &t1, NULL);
And this is the code for the handler function:
void time_handler(int signo)
{
write(STDOUT_FILENO, "interrupt\n", sizeof("interrupt\n"));
green_yield();
}
And this is what I do in the yield function: a queue from which we get the thread to run next. The problem is at any moment before I swap context between threads, I can get an interrupt. Especially because I swap the context at the end of this function.
int green_yield(){
green_t *susp = running ;
// add susp to ready queue
// ===========================
enQueue(ready_queue, susp);
// ===========================
// select the next thread for execution
// ===========================
green_t * next = deQueue(ready_queue);
running = next;
// ===========================
// save current state into susp->context and switch to next->context
// ===========================
swapcontext(susp->context, next->context);
return 0;}
What can I do to make sure that I first complete the yield function and then get the interrupt?
Foreword: Depending on your system hardware, a write() system call into stdout may take longer than 10 us. So, calling this from the SIGALRM handler with a cyclic timer of 10 us may be wrong.
In GLIBC, signal(SIGALRM, time_handler) is equivalent to sigaction() with SA_RESTART flag. SIGALRM signal is blocked during the execution of the handler. So, you will not receive a signal while running the handler. It is implicitly blocked during handler execution and unblocked after it finishes. Since the latter calls green_yield(), you will not get a signal while running inside green_yield().
As getcontext() saves the signal mask with SIGALRM unblocked (as I guess you call it at the beginning of your program when you create the threads), when you swap the context to go from one interrupted threads running the signal handler to the next schedulable thread, the newly running thread:
At 1st scheduling time, returns from its getcontext() (the thread creation point). This restores the signal mask even if the previous thread did not return from the signal handler because the context contains a signal mask with SIGALRM unblocked. When the timer elapses again, SIGALRM will come again to interrupt the newly running thread which will yield the CPU in the signal handler calling swapcontext(). This time the saved context contains a signal mask with a blocked SIGALRM;
At subsequent scheduling time, returns from swapcontext() as it was interrupted by the signal and so was running the end of the signal handler. The context restores a blocked SIGALRM signal but this will be unblocked as part of the execution of the signal handler since its execution restarts from the end of the signal handler.
Even if the preceding is supposed to work, note that when a signal is raised, the system creates a stack frame on the top of the current process stack to make the signal handler appear as a function called by the user program and returning at the interruption point. This frame on the stack must not be corrupted by threads running from any point on the global process stack. The use of sigaltstack() may be considered (see notes below).
What about your thread implementation? They all share the same stack (the process stack). When you create them, they all save their context with getcontext() nearly at the same point in the global process stack. So, when you switch from one thread to another, the newly running thread may screw up the stack frames of the previously running threads... I think this is the point on which you should focus: arrange your threads to make them run with their own global stack zone or with their own stack using something like makecontext(). The manual of the latter provides an example to create several threads of execution with separate stacks.
Side note:
swapcontext() is not part of the allowed function calls in the signal handlers: cf. man 7 signal-safety. So, it is not safe to call it from there. But at the same time, we can see that non-local gotos (i.e. longjmp()) can safely be called from the signal handler. Since swapcontext() looks like a non local goto, it may be safe to call it under the same conditions as longjmp()...
The manual of sigaltstack() provides some tips to use swapcontext() from signal handlers

Receiving SIGINT and exception Handles in Linux

Let's say we have a program in C that uses the sleep() function
The program executes and goes to sleep. Then we type Ctrl+C to send a SIGINT signal to the process.
We know that the default action upon receipt of a SIGINT is to terminate the process, we also know that the sleep() function resume the process whenever the sleeping process receives a signal.
And my textbook says in order to allow sleep() function to return, we must install a SIGINT handler like this:
void handler(int sig){
return; /* Catch the signal and return */
}
...
int main(int argc, char **argv) {
...
if (signal(SIGINT, handler) == SIG_ERR) /* Install SIGINT handler */
unix_error("signal error\n");
...
sleep(1000)
}
Althouth the code seems to be straightforward, I still have questions if I want to dig deeper:
Background: When the process is sleeping and we type Ctrl+C to send SIGINT
Q1-My understanding is, Kernel sends SIGINT to the process by updating the SIGINT's corresponging pending bit in the pend bit vector, is my understanding correct?
Q2-The processor detects the existance of SIGINT, but since we overwrite the handler to make it return in stead of terminating the process, so our handler get executed, and then Kernel clears SIGINT's corresponging pending bit, is my understanding correct?
Q3- Since SIGINT's corresponging pending bit is cleared, then how can sleep() function gets return? I think it should be in sleep still because in theory, sleep() function has no way of knowing the existance of SIGINT(has been cleared)
Q1: the kernel checks if the process has blocked the received signal, if so, it updates the pending signal bit (unreliable, on systems with relable signals, this should be a counter) in the process entry, for the signal handler to be called when signals are unblocked again (see below). If not blocked, the system call prepares the return value and errno value and returns to user mode with a special code installed in the program's virtual stack that makes it to call the signal handler (already in user mode) before returning from the generic syscall code. The return from the system call gives -1 to the caller code, and the errno variable is set to EINTR. This requires the process to have installed a signal handler, because by default the action is to abort the process, so it will not return from the system call it is waiting on. Think that when one says the kernel the actual code executed is in the system call being awaken and notified of the special condition (a signal received) The interrupted call, detects that a signal handler is to be called, and prepares the user stack to jump to the proper place (the interrupt handler in user code) before returning from the syscall() wrapper.
Q2: pending bit is only used to save that a pending signal handler is to be called, so this is not the case. In the execution part of the process, the unix program loader installs some basic code to jump to the signal handler before returning from the system call. This is because the signal handler has to execute in user mode (not in kernel mode) so everything happens upon termination of system call. The signal handler executed is the SIGINT, but the code interrupted is a system call, and nothing happens until the system call returns (with the return code and the errno variable already fixed)
Q3: well, your reasoning was based on a wrong premise, that is, the interrupt pending flag is indicating that an interrupt has been received. This bit only signals that an unprocessed interrupt has been marked for delivery as soon as you unblock it, and this only happens in another system call (to unblock a signal). As soon as the signal is unblocked, the return code of the sigsetmask(2) syscall will execute the signal handler. In this case, the signal will be delivered to the process as soon as the timer elapses, the system call will be interrupted and, if you have not installed a signal handler for the SIGALRM signal (but sleep(2) implementation does this ---at least, old implementations did) the program will be aborted.
NOTE
When I say that the program is aborted by the kernel but in both cases, the signals involved (SIGINT and SIGALRM) don't make it to dump a core file. The program is aborted without generating core. This is different to the behaviour of the abort() routine, which sends a SIGABRT and so, it makes de kernel to dump a core file of the process.
Q3- Since SIGINT's corresponging pending bit is cleared, then how can sleep() function gets return?
Imagine the sleep() function in the kernel as a function that:
allocates and sets fields in some kind of "timer event" structure
adds the "timer event" to a list of timer events for the timer's IRQ handler to worry about later (when the expiry time has elapsed)
moves the task from the "RUNNING" state to the "SLEEPING" state (so the scheduler knows not to give the task CPU time), causing scheduler to do a task switch to some other task
configures return parameters for user-space (the amount of time remaining or 0 if the time expired)
figures out why the scheduler gave it CPU time again (did the time expire or was the sleep interrupted by a signal?)
potentially mangles the stack a bit (so that the kernel returns to the signal handler if the sleep() was interrupted by a signal instead of returning to the code that called sleep())
returns to user-space
Also imagine that there's a second function (that I'm going to call wake() for no particular reason) that:
removes the "timer event" from the list of timer events (for the timer's IRQ handler to worry)
moves the task from the "SLEEPING" state to the "READY TO RUN" state (so the scheduler knows that the task can be given CPU time again)
Naturally, if the timer's IRQ handler notices that the "timer event" has expired then the timer's IRQ handler would call the wake() function to wake the task up again.
Now imagine there's a third function (that I'm going to call send_signal()) which might be called by other functions (e.g. called by kill()). This function might set a "pending signal" flag for the task that's supposed to receive the signal, then check what state the receiving task is in; and if the receiving task is in the "SLEEPING" state it calls the wake() function to wake it up (and then lets the latter part of the sleep() function worry about delivering the signal back to user-space whenever the scheduler feels like giving the task CPU time later).
Your understanding is correct.
Think about it. The process is blocked in the kernel. We need to return to user space to run the handler. How can we do that without interrupting whatever blocking kernel call was running? We only have one process/thread context to work with here. The process can't be both sleeping and running a signal handler.
The sequence is:
Process blocks in some blocking kernel call.
Signal is sent to it.
Bit is set, process is made ready-to-run.
Process resumes running in kernel mode, checks for pending non-blocked signals.
Signal dispatcher is invoked.
Process context is modified to execute signal handler upon resumption.
Process is resumed in user space
Signal handler runs.
Signal handler returns.
Kernel is invoked by end of signal handler.
Kernel makes decision whether to resume system call or return interruption error.

How are asynchronous signal handlers executed on Linux?

I would like to know exactly how the execution of asynchronous signal handlers works on Linux. First, I am unclear as to which thread executes the signal handler. Second, I would like to know the steps that are followed to make the thread execute the signal handler.
On the first matter, I have read two different, seemingly conflicting, explanations:
The Linux Kernel, by Andries Brouwer, ยง5.2 "Receiving signals" states:
When a signal arrives, the process is interrupted, the current registers are saved, and the signal handler is invoked. When the signal handler returns, the interrupted activity is continued.
The StackOverflow question "Dealing With Asynchronous Signals In Multi Threaded Program" leads me to think that Linux's behavior is like SCO Unix's:
When a signal is delivered to a process, if it is being caught, it will be handled by one, and only one, of the threads meeting either of the following conditions:
A thread blocked in a sigwait(2) system call whose argument does include the type of the caught signal.
A thread whose signal mask does not include the type of the caught signal.
Additional considerations:
A thread blocked in sigwait(2) is given preference over a thread not blocking the signal type.
If more than one thread meets these requirements (perhaps two threads are calling sigwait(2)), then one of them will be chosen. This choice is not predictable by application programs.
If no thread is eligible, the signal will remain ``pending'' at the process level until some thread becomes eligible.
Also, "The Linux Signals Handling Model" by Moshe Bar states "Asynchronous signals are delivered to the first thread found not blocking the signal.", which I interpret to mean that the signal is delivered to some thread having its sigmask not including the signal.
Which one is correct?
On the second matter, what happens to the stack and register contents for the selected thread? Suppose the thread-to-run-the-signal-handler T is in the middle of executing a do_stuff() function. Is thread T's stack used directly to execute the signal handler (i.e. the address of the signal trampoline is pushed onto T's stack and control flow goes to the signal handler)? Alternatively, is a separate stack used? How does it work?
These two explanations really aren't contradictory if you take into account the fact that Linux hackers tend to be confused about the difference between a thread and a process, mainly due to the historical mistake of trying to pretend threads could be implemented as processes that share memory. :-)
With that said, explanation #2 is much more detailed, complete, and correct.
As for the stack and register contents, each thread can register its own alternate signal-handling stack, and the process can choose on a per-signal basis which signals will be delivered on alternate signal-handling stacks. The interrupted context (registers, signal mask, etc.) will be saved in a ucontext_t structure on the (possibly alternate) stack for the thread, along with the trampoline return address. Signal handlers installed with the SA_SIGINFO flag are able to examine this ucontext_t structure if they like, but the only portable thing they can do with it is examine (and possibly modify) the saved signal mask. (I'm not sure if modifying it is sanctioned by the standard, but it's very useful because it allows the signal handler to atomically replace the interrupted code's signal mask upon return, for instance to leave the signal blocked so it can't happen again.)
Source #1 (Andries Brouwer) is correct for a single-threaded process. Source #2 (SCO Unix) is wrong for Linux, because Linux does not prefer threads in sigwait(2). Moshe Bar is correct about the first available thread.
Which thread gets the signal? Linux's manual pages are a good reference. A process uses clone(2) with CLONE_THREAD to create multiple threads. These threads belong to a "thread group" and share a single process ID. The manual for clone(2) says,
Signals may be sent to a thread group as a whole (i.e., a
TGID) using kill(2), or to a specific thread (i.e., TID) using
tgkill(2).
Signal dispositions and actions are process-wide: if an
unhandled signal is delivered to a thread, then it will affect
(terminate, stop, continue, be ignored in) all members of the
thread group.
Each thread has its own signal mask, as set by sigprocmask(2),
but signals can be pending either: for the whole process
(i.e., deliverable to any member of the thread group), when
sent with kill(2); or for an individual thread, when sent with
tgkill(2). A call to sigpending(2) returns a signal set that
is the union of the signals pending for the whole process and
the signals that are pending for the calling thread.
If kill(2) is used to send a signal to a thread group, and the
thread group has installed a handler for the signal, then the
handler will be invoked in exactly one, arbitrarily selected
member of the thread group that has not blocked the signal.
If multiple threads in a group are waiting to accept the same
signal using sigwaitinfo(2), the kernel will arbitrarily
select one of these threads to receive a signal sent using
kill(2).
Linux is not SCO Unix, because Linux might give the signal to any thread, even if some threads are waiting for a signal (with sigwaitinfo, sigtimedwait, or sigwait) and some threads are not. The manual for sigwaitinfo(2) warns,
In normal usage, the calling program blocks the signals in set via a
prior call to sigprocmask(2) (so that the default disposition for
these signals does not occur if they become pending between
successive calls to sigwaitinfo() or sigtimedwait()) and does not
establish handlers for these signals. In a multithreaded program,
the signal should be blocked in all threads, in order to prevent the
signal being treated according to its default disposition in a thread
other than the one calling sigwaitinfo() or sigtimedwait()).
The code to pick a thread for the signal lives in linux/kernel/signal.c (the link points to GitHub's mirror). See the functions wants_signal() and completes_signal(). The code picks the first available thread for the signal. An available thread is one that doesn't block the signal and has no other signals in its queue. The code happens to check the main thread first, then it checks the other threads in some order unknown to me. If no thread is available, then the signal is stuck until some thread unblocks the signal or empties its queue.
What happens when a thread gets the signal? If there is a signal handler, then the kernel causes the thread to call the handler. Most handlers run on the thread's stack. A handler can run on an alternate stack if the process uses sigaltstack(2) to provide the stack, and sigaction(2) with SA_ONSTACK to set the handler. The kernel pushes some things onto the chosen stack, and sets some of the thread's registers.
To run the handler, the thread must be running in userspace. If the thread is running in the kernel (perhaps for a system call or a page fault), then it does not run the handler until it goes to userspace. The kernel can interrupt some system calls, so the thread runs the handler now, without waiting for the system call to finish.
The signal handler is a C function, so the kernel obeys the architecture's convention for calling C functions. Each architecture, like arm, i386, powerpc, or sparc, has its own convention. For powerpc, to call handler(signum), the kernel sets the register r3 to signum. The kernel also sets the handler's return address to the signal trampoline. The return address goes on the stack or in a register by convention.
The kernel puts one signal trampoline in each process. This trampoline calls sigreturn(2) to restore the thread. In the kernel, sigreturn(2) reads some information (like saved registers) from the stack. The kernel had pushed this information on the stack before calling the handler. If there was an interrupted system call, the kernel might restart the call (only if the handler used SA_RESTART), or fail the call with EINTR, or return a short read or write.

How do unix signals work?

How do signals work in unix? I went through W.R. Stevens but was unable to understand. Please help me.
The explanation below is not exact, and several aspects of how this works differ between different systems (and maybe even the same OS on different hardware for some portions), but I think that it is generally good enough for you to satisfy your curiosity enough to use them. Most people start using signals in programming without even this level of understanding, but before I got comfortable using them I wanted to understand them.
signal delivery
The OS kernel has a data structure called a process control block for each process running which has data about that process. This can be looked up by the process id (PID) and included a table of signal actions and pending signals.
When a signal is sent to a process the OS kernel will look up that process's process control block and examines the signal action table to locate the action for the particular signal being sent. If the signal action value is SIG_IGN then the new signal is forgotten about by the kernel. If the signal action value is SIG_DFL then the kernel looks up the default signal handling action for that signal in another table and preforms that action. If the values are anything else then that is assumed to be a function address within the process that the signal is being sent to which should be called. The values for SIG_IGN and SIG_DFL are numbers cast to function pointers whose values are not valid addresses within a process's address space (such as 0 and 1, which are both in page 0, which is never mapped into a process).
If a signal handling function were registered by the process (the signal action value was neither SIG_IGN or SIG_DFL) then an entry in the pending signal table is made for that signal and that process is marked as ready to RUN (it may have been waiting on something, like data to become available for a call to read, waiting for a signal, or several other things).
Now the next time that the process is run the OS kernel will first add some data to the stack and changes the instruction pointer for that process so that it looks almost like the process itself has just called the signal handler. This is not entirely correct and actually deviates enough from what actually happens that I'll talk about it more in a little bit.
The signal handler function can do whatever it does (it is part of the process that it was called on behalf of, so it was written with knowledge about what that program should do with that signal). When the signal handler returns then the regular code for the process begins executing again. (again, not accurate, but more on that next)
Ok, the above should have given you a pretty good idea of how signals are delivered to a process. I think that this pretty good idea version is needed before you can grasp the full idea, which includes some more complicated stuff.
Very often the OS kernel needs to know when a signal handler returns. This is because signal handlers take an argument (which may require stack space), you can block the same signal from being delivered twice during the execution of the signal handler, and/or have system calls restarted after a signal is delivered. To accomplish this a little bit more than stack and instruction pointer changes.
What has to happen is that the kernel needs to make the process tell it that it has finished executing the signal handler function. This may be done by mapping a section of RAM into the process's address space which contains code to make this system call and making the return address for the signal handler function (the top value on the stack when this function started running) be the address of this code. I think that this is how it is done in Linux (at least newer versions). Another way to accomplish this (I don't know if this is done, but it could be) would be do make the return address for the signal handler function be an invalid address (such as NULL) which would cause an interrupt on most systems, which would give the OS kernel control again. It doesn't matter a whole lot how this happens, but the kernel has to get control again to fix up the stack and know that the signal handler has completed.
WHILE LOOKING INTO ANOTHER QUESTION I LEARNED
that the Linux kernel does map a page into the process for this, but that the actual system call for registering signal handlers (what sigaction calls ) takes a parameter sa_restore parameter, which is an address that should be used as the return address from the signal handler, and the kernel just makes sure that it is put there. The code at this address issues the I'm done system call (sigreturn)and the kernel knows that the signal handler has finished.
signal generation
I'm mostly assuming that you know how signals are generated in the first place. The OS can generate them on behalf of a process due to something happening, like a timer expiring, a child process dying, accessing memory that it should not be accessing, or issuing an instruction that it should not (either an instruction that does not exist or one that is privileged), or many other things. The timer case is functionally a little different from the others because it may occur when the process is not running, and so is more like the signals sent with the kill system call. For the non-timer related signals sent on behalf of the current process these are generated when an interrupt occurs because the current process is doing something wrong. This interrupt gives the kernel control (just like a system call) and the kernel generates the signal to be delivered to the current process.
Some issues that are not addressed in all of the above statements are multi core, running in kernel space while receiving a signal, sleeping in kernel space while receiving a signal, system call restarting and signal handler latency.
Here are a couple of issues to consider:
What if the kernel knows that a signal needs to be delivered to process X which is running on CPU_X, but the kernel learns about it while running on CPU_Y (CPU_X!=CPU_Y). So the kernel needs to stop the process from running on a different core.
What if the process is running in kernel space while receiving a signal? Every time a process makes a system call it enters kernel space and tinkers with data structures and memory allocations in kernel space. Does all of this hacking take place in kernel space too?
What if the process is sleeping in kernel space waiting for some other event? (read, write, signal, poll, mutex are just some options).
Answers:
If the process is running on another CPU the kernel, via cross CPU communication, will deliver an interrupt to the other CPU and a message for it. The other CPU will, in hardware, save state and jump to the kernel on the other CPU and then will do the delivery of the signal on the other CPU. This is all a part of trying not to execute the signal handler of the process on another CPU which will break cache locality.
If the process is running in kernel space it is not interrupted. Instead it is recorded that this process has received a signal. When the process exits kernel space (at the end of each system call), the kernel will setup the trampoline to execute the signal handler.
If the process, while running in kernel space, after having received a signal, reaches a sleep function, then that sleep function (and this is common to all sleep functions within the kernel) will check if the process has a signal pending. If it is so, it will not put the process to sleep and instead will cancel all that has been done while coming down into the kernel, and will exit to user space while setting up a trampoline to execute the signal handler and then restart the system call. You can actually control which signals you want to interrupt system calls and which you do not using the siginterrupt(2) system call. You can decide if you want system calls restartable for a certain signal when you register the signal using sigaction(2) with the SA_RESTART flag. If a system call is issued and is cut off by a signal and is not restarted automatically you will get an EINTR (interrupted) return value and you must handle that value. You can also look at the restart_syscall(2) system call for more details.
If the process is already sleeping/waiting in kernel space (actually all sleeping/waiting is always in kernel space) it is woken from the sleep, kernel code cleans up after itself and jump to signal handler on return to user space after which the system call is automatically restarted if the user so desired (very similar to previous explanation of what happens if the process is running in kernel space).
A few notes about why all of this is so complex:
You cannot just stop a process running in kernel space since the kernel developer allocates memory, does things to data structures and more. If you just take the control away you will corrupt the kernel state and cause a machine hang. The kernel code must be notified in a controlled way that it must stop its running, return to user space and allow user space to handle the signal. This is done via the return value of all (well, almost all) sleeping functions in the kernel. And kernel programmers are expected to treat those return values with respect and act accordingly.
Signals are asynchronous. This means that they should be delivered as soon as possible. Imagine a process that has only one thread, went to sleep for hour, and is delivered a signal. Sleep is inside the kernel. So you except the kernel code to wake up, clean up after itself, return to user space and execute the signal handler, possibly restarting the system call after the signal handler finished. You certainly do not expect that process to only execute the signal handler an hour later. Then you expect the sleep to resume. Great trouble is taken by the user space and kernel people to allow just that.
All in all signals are like interrupt handlers but for user space. This is a good analogy but not perfect. While interrupt handlers are generated by hardware some signal handlers originate from hardware but most are just software (signal about a child process dying, signal from another process using the kill(2) syscall and more).
So what is the latency of signal handling?
If when you get a signal some other process is running then it up to the kernel scheduler to decide if to let the other process finish its time slice and only then deliver the signal or not. If you are on a regular Linux/Unix system this means that you could be delayed by 1 or more time slices before you get the signal (which means milliseconds which are equivalent to eternity).
When you get a signal, if your process is high-priority or other processes already got their time slice you will get the signal quite fast. If you are running in user space you will get it "immediately", if you are running in kernel space you will shortly reach a sleep function or return from kernel in which case when you return to user space your signal handler will be called. That is usually a short time since not a lot of time is spent in the kernel.
If you are sleeping in the kernel, and nothing else is above your priority or needs to run, the kernel thread handling your system call is woken up, cleans up after all the stuff it did on the way down into the kernel, goes back to user space and executes your signal. This doesn't take too long (were talking microseconds here).
If you are running a real time version of Linux and your process has the highest real time priority then you will get the signal very soon after it is triggered. Were talking 50 microseconds or even better (depends on other factors that I cannot go into).
Think of the signal facility as interrupts, implemented by the OS (instead of in hardware).
As your program merrily traverses its locus of execution rooted in main(), these interrupts can occur, cause the program to be dispatched to a vector (handler), run the code there, and then return to the location where it got interrupted.
These interrupts (signals) can originate from a variety of sources e.g. hardware errors like accessing bad or misaligned addresses, death of a child process, user generated signals using the kill command, or from other processes using the kill system call. The way you consume signals is by designating handlers for them, which are dispatched by the OS when the signals occur. Note that some of these signals cannot be handled, and result in the process simply dying.
But those that can be handled, can be quite useful. You can use them for inter process communication i.e. one process sends a signal to another process, which handles it, and in the handler does something useful. Many daemons will do useful things like reread the configuration file if you send them the right signal.
Signal are nothing but an interrupt in the execution of the process. A process can signal itself or it can cause a signal to be passed to another process. Maybe a parent can send a signal to its child in order to terminate it, etc..
Check the following link to understand.
https://unix.stackexchange.com/questions/80044/how-signals-work-internally
http://www.linuxjournal.com/article/3985
http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show

Resources