As
stated in the introduction of this chapter, programs running in User
Mode are allowed to send and receive signals. This means that a set
of system calls must be defined to allow these kinds of operations.
Unfortunately, for historical reasons, several system calls exist
that serve essentially the same purpose. As a result, some of these
system calls are never invoked. For instance, sys_sigaction( )
and sys_rt_sigaction( )
are almost
identical, so the sigaction( )
wrapper function
included in the C library ends up invoking sys_rt_sigaction( )
instead of sys_sigaction( )
. We shall
describe some of the most significant POSIX system calls.
The kill(pid,sig)
system call is commonly used to
send signals; its corresponding service routine is the
sys_kill( )
function. The integer
pid
parameter has several meanings, depending on
its numerical value:
The sig
signal is sent to the process whose PID is
equal to pid
.
The sig
signal is sent to all processes in the
same group as the calling process.
The signal is sent to all processes, except
swapper (PID 0), init (PID
1), and current
.
The signal is sent to all processes in the process group -pid.
The sys_kill( )
function sets up a minimal
siginfo_t
table for the signal, and then invokes
kill_something_info( )
:
info.si_signo = sig; info.si_errno = 0; info.si_code = SI_USER; info._sifields._kill._pid = current->pid; info._sifields._kill._uid = current->uid; return kill_something_info(sig, &info, pid);
The kill_something_info( )
function, in turn,
invokes either send_sig_info( )
(to send the
signal to a single process), or kill_pg_info( )
(to scan all processes and invoke send_sig_info( )
for each process in the destination group).
The kill( )
system call is able to send any
signal, even the so-called real-time signals that have numbers
ranging from 32 to 63. However, as we saw in the earlier section
Section 10.2, the kill( )
system call does not ensure that a new element is added
to the pending signal queue of the destination process, thus multiple
instances of pending signals can be lost. Real-time signals should be
sent by means of a system call like rt_sigqueueinfo( )
(see the later section Section 10.4.6).
System V and BSD Unix variants also have a killpg( )
system call, which is able to explicitly send a signal to
a group of processes. In Linux, the function is implemented as a
library function that uses the kill( )
system
call. Another variation is raise( )
, which sends a
signal to the current process (that is, to the process executing the
function). In Linux, raise()
is implemented as a
library function.
The
sigaction(sig,act,oact)
system call allows users
to specify an action for a signal; of course, if no signal action is
defined, the kernel executes the default action associated with the
delivered signal.
The corresponding sys_sigaction( )
service routine
acts on two parameters: the sig
signal number and
the act
table of type sigaction
that specifies the new action. A third oact
optional output parameter may be used to get the previous action
associated with the signal.
The function checks first whether the act
address
is valid. Then it fills the sa_handler
,
sa_flags
, and sa_mask
fields of
a new_ka
local variable of type
k_sigaction
with the corresponding fields of
*act
:
_ _get_user(new_ka.sa.sa_handler, &act->sa_handler); _ _get_user(new_ka.sa.sa_flags, &act->sa_flags); _ _get_user(mask, &act->sa_mask); siginitset(&new_ka.sa.sa_mask, mask);
The function invokes do_sigaction( )
to copy the
new new_ka
table into the entry at the
sig
-1 position of
current->sig->action
(
the
number of the signal is one higher than the position in the array
because there is no zero signal):
k = ¤t->sig->action[sig-1]; spin_lock(¤t->sig->siglock); if (act) { *k = *act; sigdelsetmask(&k->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP)); if (k->sa.sa_handler == SIG_IGN || (k->sa.sa_handler == SIG_DFL && (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH))) { spin_lock_irq(¤t->sigmask_lock); if (rm_sig_from_queue(sig, current)) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } }
The POSIX standard requires that setting a signal action to either
SIG_IGN
or SIG_DFL
when the
default action is “ignore,” causes
any pending signal of the same type to be discarded. Moreover, notice
that no matter what the requested masked signals are for the signal
handler, SIGKILL
and SIGSTOP
are never masked.
If the oact
parameter is not
NULL
, the contents of the previous
sigaction
table are copied to the process address
space at the address specified by that parameter:
if (oact) { _ _put_user(old_ka.sa.sa_handler, &oact->sa_handler); _ _put_user(old_ka.sa.sa_flags, &oact->sa_flags); _ _put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); }
Notice that the sigaction( )
system call also
allows initialization of the sa_flags
field in the
sigaction
table. We listed the values allowed for
this field and the related meanings in Table 10-4
(earlier in this chapter).
Older System V Unix variants offered the signal( )
system call, which is still widely used by programmers. Recent C
libraries implement signal( )
by means of
sigaction( )
. However, Linux still supports older
C libraries and offers the sys_signal( )
service
routine:
new_sa.sa.sa_handler = handler; new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK; ret = do_sigaction(sig, &new_sa, &old_sa); return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
The sigpending( )
system call allows a process to
examine the set of
pending blocked
signals—i.e., those that have been raised while blocked. The
corresponding sys_sigpending( )
service routine
acts on a single parameter, set
, namely, the
address of a user variable where the array of bits must be copied:
spin_lock_irq(¤t->sigmask_lock); sigandsets(&pending, ¤t->blocked, ¤t->pending.signal); spin_unlock_irq(¤t->sigmask_lock); copy_to_user(set, &pending, sizeof(sigset_t));
The
sigprocmask( )
system call allows processes to
modify the set of blocked signals; it applies only to regular
(non-real-time) signals. The corresponding sys_sigprocmask( )
service routine acts on three parameters:
oset
Pointer in the process address space to a bit array where the previous bit mask must be stored
set
Pointer in the process address space to the bit array containing the new bit mask
how
Flag that may have one of the following values:
SIG_BLOCK
The *set
bit mask array specifies the signals that
must be added to the bit mask array of blocked signals.
SIG_UNBLOCK
The *set
bit mask array specifies the signals that
must be removed from the bit mask array of blocked signals.
SIG_SETMASK
The *set
bit mask array specifies the new bit mask
array of blocked signals.
The function invokes copy_from_user( )
to copy the
value pointed to by the set
parameter into the
new_set
local variable and copies the bit mask
array of standard blocked signals of current
into
the old_set
local variable. It then acts as the
how
flag specifies on these two variables:
if (copy_from_user(&new_set, set, sizeof(*set))) return -EFAULT; new_set &= ~(sigmask(SIGKILL)|sigmask(SIGSTOP)); spin_lock_irq(¤t->sigmask_lock); old_set = current->blocked.sig[0]; if (how == SIG_BLOCK) sigaddsetmask(¤t->blocked, new_set); else if (how == SIG_UNBLOCK) sigdelsetmask(¤t->blocked, new_set); else if (how == SIG_SETMASK) current->blocked.sig[0] = new_set; else return -EINVAL; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); if (oset) { if (copy_to_user(oset, &old_set, sizeof(*oset))) return -EFAULT; } return 0;
The
sigsuspend( )
system call puts the process in the
TASK_INTERRUPTIBLE
state, after having blocked the
standard signals specified by a bit mask array to which the
mask
parameter points. The process will wake up
only when a nonignored, nonblocked signal is sent to it.
The corresponding sys_sigsuspend( )
service
routine executes these statements:
mask &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP)); spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->eax = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule( ); if (do_signal(regs, &saveset)) return -EINTR; }
The schedule( )
function selects another process
to run. When the process that issued the sigsuspend( )
system call is executed again, sys_sigsuspend( )
invokes the do_signal( )
function to
deliver the signal that has woken up the process. If that function
returns the value 1, the signal is not ignored. Therefore the system
call terminates by returning the error code
-EINTR
.
The sigsuspend( )
system call may appear
redundant, since the combined execution of sigprocmask( )
and sleep( )
apparently yields the
same result. But this is not true: because of interleaving of process
executions, one must be conscious that invoking a system call to
perform action A followed by another system call to perform action B
is not equivalent to invoking a single system call that performs
action A and then action B.
In the particular case, sigprocmask( )
might
unblock a signal that is delivered before invoking sleep( )
. If this happens, the process might remain in a
TASK_INTERRUPTIBLE
state forever, waiting for the
signal that was already delivered. On the other hand, the
sigsuspend( )
system call does not allow signals
to be sent after unblocking and before the schedule( )
invocation because other processes cannot grab the CPU
during that time interval.
Since the system calls previously examined apply only to standard signals, additional system calls must be introduced to allow User Mode processes to handle real-time signals.
Several system calls for real-time signals (rt_sigaction( )
, rt_sigpending( )
,
rt_sigprocmask( )
, and rt_sigsuspend( )
) are similar to those described earlier and
won’t be discussed further. For the same reason, we
won’t discuss two other system calls that deal with
queues of real-time signals:
rt_sigqueueinfo( )
Sends a real-time signal so that it is added to the pending signal queue of the destination process
rt_sigtimedwait( )
Dequeues a blocked pending signal without delivering it and returns the signal number to the caller; if no blocked signal is pending, suspends the current process for a fixed amount of time.
18.117.187.113