When
a signal is sent to a process, either from the kernel or from another
process, the kernel generates it by invoking the
send_sig_info( )
, send_sig( )
,
force_sig( )
, or force_sig_info( )
functions. These accomplish the first phase of signal
handling described earlier in Section 10.1, updating the process
descriptor as needed. They do not directly perform the second phase
of delivering the signal but, depending on the type of signal and the
state of the process, may wake up the process and force it to receive
the signal.
The send_sig_info( )
function acts on three parameters:
sig
The signal number.
info
Either the address of a siginfo_t
table or one of
two special values. 0 means that the signal has been sent by a User
Mode process, while 1 means that it has been sent by the kernel.
t
A pointer to the descriptor of the destination process.
The send_sig_info( )
function starts by checking
whether the parameters are correct:
if (sig < 0 || sig > 64) return -EINVAL;
The function then checks if the signal is being sent by a User Mode
process. This occurs when info
is equal to 0 or
when the si_code
field of the
siginfo_t
table is negative or 0 (positive values
of this field mean that the signal was sent by some kernel function):
if ((!info || ((unsigned long)info != 1 && (info->si_code <=0))) && ((sig != SIGCONT) || (current->session != t->session)) && (current->euid ^ t->suid) && (current->euid ^ t->uid) && (current->uid ^ t->suid) && (current->uid ^ t->uid) && !capable(CAP_KILL)) return -EPERM;
If the signal is sent by a User Mode process, the function determines whether the operation is allowed. The signal is delivered only if at least one of the following conditions holds:
The owner of the sending process has the proper capability (usually, this simply means the signal was issued by the system administrator; see Chapter 20).
The signal is SIGCONT
and the destination process
is in the same login session of the sending process.
Both processes belong to the same user.
If the sig
parameter has the value 0, the function
returns immediately without generating any signal. Since 0 is not a
valid signal number, it is used to allow the sending process to check
whether it has the required privileges to send a signal to the
destination process. The function also returns if the destination
process is in the TASK_ZOMBIE
state, indicated by
checking whether its siginfo_t
table has been
released:
if (!sig || !t->sig) return 0;
Now the kernel has finished the preliminary checks, and it is going to fiddle with the signal-related data structures. To avoid race conditions, it disables the interrupts and acquires the signal spin lock of the destination process:
spin_lock_irqsave(&t->sigmask_lock, flags);
Some types of signals might nullify other pending signals for the destination process. Therefore, the function checks whether one of the following cases occurs:
sig
is a SIGKILL
or
SIGCONT
signal. If the destination process is
stopped, it is put in the TASK_RUNNING
state so
that it is able to either execute the do_exit( )
function or just continue its execution; moreover, if the destination
process has SIGSTOP
, SIGTSTP
,
SIGTTOU
, or SIGTTIN
pending
signals, they are removed:
if (t->state == TASK_STOPPED) wake_up_process(t); t->exit_code = 0; rm_sig_from_queue(SIGSTOP, t); rm_sig_from_queue(SIGTSTP, t); rm_sig_from_queue(SIGTTOU, t); rm_sig_from_queue(SIGTTIN, t);
The rm_sig_from_queue( )
function clears the bit
in t->pending.signal
associated with the signal
number passed as first argument and removes any item in the pending
signal queue of the process that corresponds to that signal number.
sig
is a SIGSTOP
,
SIGTSTP
, SIGTTIN
, or
SIGTTOU
signal. If the destination process has a
pending SIGCONT
signal, it is removed:
rm_sig_from_queue(SIGCONT, t);
Next, send_sig_info( )
checks whether the new
signal can be handled immediately. In this case, the function also
takes care of the delivering phase of the signal:
if (ignored_signal(sig, t)) { spin_unlock_irqrestore(&t->sigmask_lock, flags); return 0; }
The ignored_signal( )
function returns the value 1
when all three conditions for ignoring a signal that are mentioned in
Section 10.1 are
satisfied. However, to fulfill a POSIX requirement, the
SIGCHLD
signal is handled specially. POSIX
distinguishes between explicitly setting the
“ignore” action for the
SIGCHLD
signal and leaving the default in place
(even if the default is to ignore the signal). To let the kernel
clean up a terminated child process and prevent it from becoming a
zombie (see Section 3.5.2), the parent
must explicitly set the action to
“ignore” the signal. So
ignored_signal( )
handles this case as follows: if
the signal is explicitly ignored, ignored_signal( )
returns 0, but if the default action was
“ignore” and the process
didn’t change that default, ignored_signal( )
returns 1.
If ignored_signal( )
returns 1, the
siginfo_t
table of the destination process must
not be updated, and the send_sig_info( )
function
terminates. Since the signal is no longer pending, it has been
effectively delivered to the destination process, even if the process
never sees it.
If ignored_signal( )
returns 0, the phase of
signal delivering has to be deferred, therefore
send_sig_info( )
may have to modify the data
structures of the destination process to let it know later that a new
signal has been sent to it. However, if the signal being handled was
already pending, the send_sig_info( )
function can
simply terminate. In fact, there can be at most one occurrence of any
regular signal type in the pending signal queue of a process because
regular signal occurrences are not really queued:
if (sig < 32 && sigismember(&t->pending.signal, sig)) { spin_unlock_irqrestore(&t->sigmask_lock, flags); return 0; }
If it proceeds, the send_sig_info( )
function must
insert a new item in the pending signal queue of the destination
process. This is achieved by invoking the send_signal( )
function:
retval = send_signal(sig, info, &t->pending);
In turn, the send_signal( )
function checks the
length of the pending signal queue and appends a new
sigqueue
data structure:
if (atomic_read(&nr_queued_signals) < max_queued_signals) { q = kmem_cache_alloc(sigqueue_cachep, GFP_ATOMIC); atomic_inc(&nr_queued_signals); q->next = NULL; *(t->pending.tail) = q; t->pending.tail = &q->next;
Then the send_sig_info( )
function fills the
siginfo_t
table inside the new queue item:
if ((unsigned long)info == 0) { q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_USER; q->info._sifields._kill._pid = current->pid; q->info._sifields._kill._uid = current->uid; } else if ((unsigned long)info == 1) { q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_KERNEL; q->info._sifields._kill._pid = 0; q->info._sifields._kill._uid = 0; } else copy_siginfo(&q->info, info); }
The info
argument passed to the
send_signal( )
function either points to a
previously built siginfo_t
table or stores the
constants 0 (for a signal sent by a User Mode process) or 1 (for a
signal sent by a kernel function).
If it is not possible to add an item to the queue, either because it
already includes max_queued_signals
elements or
because there is no free memory for the sigqueue
data structure, the signal occurrence cannot be queued. If the signal
is real-time and was sent through a system call that is explicitly
required to queue it (like rt_sigqueueinfo( )
),
the send_signal( )
function returns an error code.
Otherwise, it sets the corresponding bit in
t->pending.signal
:
if (sig >= 32 && info && (unsigned long)info != 1 && info->si_code != SI_USER) return -EAGAIN; sigaddset(&t->pending.signal, sig); return 0;
It is important to let the destination process receive the signal
even if there is no room for the corresponding item in the pending
signal queue. Suppose, for instance, that a process is consuming too
much memory. The kernel must ensure that the kill( )
system call succeeds even if there is no free memory;
otherwise, the system administrator doesn’t have any
chance to recover the system by terminating the offending process.
If the send_signal( )
function successfully
terminated and the signal is not blocked, the destination process has
a new pending signal to consider:
if (!retval && !sigismember(&t->blocked, sig)) signal_wake_up(t);
The signal_wake_up( )
function performs three
actions:
Sets the sigpending
flag of the destination
process.
Checks whether the destination process is already running on another
CPU and, in this case, sends an interprocessor interrupt to that CPU
to force a reschedule of the current process (see Section 4.6.2). Since each process checks the existence of
pending signals when returning from the schedule( )
function, the interprocessor interrupt ensures that the
destination process quickly notices the new pending signal if it is
already running.
Checks whether the destination process is in the
TASK_INTERRUPTIBLE
state and, in this case, wakes
it up by invoking the wake_up_process( )
.
Finally, the send_sig_info( )
function re-enables
the interrupts, releases the spin lock, and terminates with the error
code of send_signal( )
:
spin_unlock_irqrestore(&t->sigmask_lock, flags); return retval;
The send_sig( )
function is similar to
send_sig_info( )
. However, the
info
parameter is replaced by a
priv
flag, which is 1 if the signal is sent by the
kernel and 0 if it is sent by a process. The send_sig( )
function is implemented as a special case of
send_sig_info( )
:
return send_sig_info(sig, (void*)(long)(priv != 0), t);
The force_sig_info(sig, info, t)
function is used by the kernel to send signals
that cannot be explicitly ignored or blocked by the destination
processes. The function’s parameters are the same as
those of send_sig_info( )
. The
force_sig_info( )
function acts on the
signal_struct
data structure that is referenced by
the sig
field included in the descriptor
t
of the destination process:
spin_lock_irqsave(&t->sigmask_lock, flags); if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN) t->sig->action[sig-1].sa.sa_handler = SIG_DFL; sigdelset(&t->blocked, sig); recalc_sigpending(t); spin_unlock_irqrestore(&t->sigmask_lock, flags); return send_sig_info(sig, info, t);
force_sig( )
is similar to
force_sig_info( )
. Its use is limited to signals
sent by the kernel; it can be implemented as a special case of the
force_sig_info( )
function:
force_sig_info(sig, (void*)1L, t);
52.15.231.106