Signal Anticipation

   

Signal anticipation is what an application can do to plan for signals. From the application's point of view, the preferred method of planning for signals is with the sigaction() system call. Other system calls are available too, but they have essentially the same functionality. Sigaction() is the POSIX standard and should be used in all new development.

Sigaction() expects three arguments: the signal number, a struct sigaction containing new values to be used for that signal, and a struct sigaction into which the old values can be returned. The file /usr/include/sys/signal.h includes symbolic definitions of the signal numbers, the definition of the sigaction structure, and a variety of other signal-related declarations. The sigaction structure contains a pointer to a handler routine, a mask that indicates what signals should be blocked while handling this signal, and a set of flags for modifying the signal-handling behavior. We'll see how these are used shortly.

If the user has specified a non-NULL value for the second argument, indicating that he or she wants to change the existing values for the signal, sigaction() first does a few sanity checks to make sure that the values passed in the sigaction structure are valid. We first check to see if the user is trying to change the action for SIGKILL, SIGSTOP, or SIGCANCEL. These signals cannot be caught. Any attempt to change the handler to anything other than SIG_DFL (the default handler) will cause sigaction() to return EINVAL. Next, we validate the flags to make sure that there are no unsupported bits set in the flags field. If any bits other than those defined in signal.h are set, sigaction() will again return EINVAL. Finally, if the user is trying to change the behavior of the SIGGFAULT signal, he or she must also have the _SA_SIGGFAULT flag set in the flags field. SIGGFAULT is used by the graphics libraries and should normally not be changed by a user application. This flag is a way of saying, "Yes, I really do want to change SIGGFAULT."

Once we've passed the sanity checks, the next thing sigaction() does is call setsigvec_setup(). This routine is responsible for allocating any needed memory and for acquiring any locks needed to ensure that the operation will be atomic and can't be interrupted. Specifically, if the SA_SIGINFO flag is set, then the kernel must allocate memory in which to store signal information to pass to the signal handler. This is done first, before any locks are acquired, because it has to MALLOC memory, and MALLOC might sleep if memory is not available. Next, if the signal being modified is SIGCLD, we obtain three locks: the MTPROC_REAP_LOCK, the systemwide sched_lock spinlock, and the exit_lock spinlock for the current process. This is because SIGCLD changes modify the behavior taken when a child exits. The MTPROC_REAP_LOCK makes sure that the process isn't currently being reaped; the sched_lock makes sure the scheduler can't modify it; the exit_lock makes sure that it's not currently trying to exit. Finally, we obtain the spinlock that controls the process structure: p->p_lock. Once all this is done, we're back to sigaction().

The actual setting and retrieving of the signal handler values is really pretty straightforward. If the user has passed a non-NULL pointer for the third argument to sigaction, indicating that he or she wants to get the current values, we just extract the handler and flag values from the proc structure (and its associated Process_Shared_Fields structure) and pass them back to the user. Sigaction then calls sigsetvec to set the new values, which again is pretty straightforward. The handler is copied to p->p_shared->p_signal[sig-1]. The mask is copied into p->p_shared->p_sighandmask[sig-1]. We then go through the flags value and copy each of the flags into its correct place for most of the flags, this is a bit in p->p_shared.

There are a few special cases in setsigvec(). The SA_NOCLDSTOP and SA_NOCLDWAIT flags are looked at only if the signal number is SIGCLD. If the request is turning off the SA_SIGINFO flag, we go through each non-zombie child of the current process and delete the structure that is holding the siginfo data. If the action is being set to SIG_IGN, indicating that the user wants to ignore this signal, setsigvec() goes through all threads and removes any pending or blocked signals for this signal number.

There are also bitfields in the proc structure that get updated at this point. p->p_sigignore is a ksigset_t that says which signals are ignored, and p->p_sigcatch is a ksigset_t that says which signals are being caught. Note that these are actually in the proc structure itself, not in the Process_Shared_Fields.

Finally, after all this is done, sigaction() calls setsigvec_cleanup() to release any locks acquired in setsigvec_setup().

We now have our signal handler set up we saved off what signal the user is interested in, what he or she wants to do with that signal, and a set of flags and a mask that modify what happens when the signal arrives. We look in more detail at how these flags and masks are handled when we look at signal handling. But first, let's look at how signals get delivered.



HP-UX 11i Internals
HP-UX 11i Internals
ISBN: 0130328618
EAN: 2147483647
Year: 2006
Pages: 167

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net