First, the basic (since forever) unix signal handling method, then some NetBSD code pointers (and I'm sure you will find the rest).
Signal generation (causing a signal to be sent to a process) you have largely found I believe - either a sys call (from the process to receive the signal, or another) or some async event (interrupt from the clock, or some device driver) decides a signal needs to be delivered to a process, and arranges to queue it (it used to be just set a bit in the signal pending word, but I'm sure it is more complex these days). That code also takes care of doing nothing should the signal not be wanted by the receiving process. Next note, that if we're doing this, kernel code is obviously running, so before any user code can execute again, the kernel needs to execute the "return to user space" function(s). Part of that is selecting which process runs next (if we have actually delivered a signal to a process it will have been made runnable, as you noted, so that process is a candidate for being the next - or one of the next on a multiprocessor - process to be given the cpu). One of the steps in returning to user space is to look and see if there are any pending signals. If there are, rather than returning to where the process was previously executing, an "interrupt" stack context is created for the process, and it is set to resume at its signal handler - that step is obviously highly machine (and emulation) dependent. Then when the kernel returns to user space, the user process will run is signal handler. In NetBSD, return to user space is (partly) lwp_userret() in kern/kern_lwp.c In there you will see... if ((l->l_flag & (LW_PENDSIG | LW_WCORE | LW_WEXIT)) == LW_PENDSIG) { mutex_enter(p->p_lock); while ((sig = issignal(l)) != 0) postsig(sig); mutex_exit(p->p_lock); } That is checking if there is a pending signal, and if so, deliver it to the process - you can follow postsig() to see how it gets down into the arch/emul specific sig setup routine to actually set the environ for the user process, and issignal() to see how the pending signals are examined and one is selected to deliver first (but think carefully about just what the while loop quoted above actually means...) Both of those are in kern/kern_sig.c kre