Before we get into the process of handling signals, let's look at the associated data structures. Figure 14-1 shows the various signal-related structures and how they relate to the proc and kthread structures. Figure 14-1. Signal-Related Data Structures With the advent of multithreaded processes in HP-UX version 11.0, the concept of signals became much more complicated, and several new data structures were created. At the same time, much of the data that used to be kept in the proc structure was moved into a new structure called a struct Process_Shared_Fields. The proc structure has a field, p_shared, that points to the Process_Shared_Fields structure. The Process_Shared_Fields is mostly made up of a group of fields of type ksigset_t, which is merely an array of integers used as a bitfield one bit per signal type. A variety of routines exist in the kernel for manipulating these bitfields setting or clearing individual bits, setting or clearing all bits, testing bits, and so on. The Process_Shared_fields structure is shown in Listing 14.1. Listing 14.1. Process_Shared_fields structure struct Process_Shared_Fields { ksigset_t pshared_sigignore; /* signals being ignored */ ksigset_t pshared_sigcatch; /* signals being caught by user */ int pshared_nsig; /* # of signals recognizedby process */ void (*pshared_signal[_NSIG-1])(); /* disposition of signals */ ksigset_t pshared_sighandmask[_NSIG-1]; /* signals to be blocked */ ksigset_t pshared_sigreset; /* reset handler after catching */ ksigset_t pshared_siginfowanted;/* sigs to be delivered witv siginfo*/ ksigset_t pshared_sigrestart; /* restart interrupted sys call */ ksigset_t pshared_signodefer; /* Do not block sig in handler */ ksigset_t pshared_sigonstack; /* Signals to take on sigstack */ void (*pshared_sigreturn)(); /* handler return address proc_sigcontexttype_t pshared_sigcontexttype; }; In addition to the above structure, there are a few signal-related fields that are still in the proc structure (Listing 14.2). Listing 14.2. Signal-related fields in proc structure struct proc { ... ksigset_t p_sig; /* sigs pending on the proc */ ksigset_t p_ksi_avail; /* signals with siginfo available */ ksigset_t p_ksifl_alloced; /* signals with freelist entries */ ksi_t *p_ksiactive; /* active list of pending signals */ ksi_t *p_ksifree; /* free list of ksi_t's */ struct sigcount *p_sigcountp; /* # signals pending at receivers */ ksigwait_t *p_sigwaiters; /* list of sigwaiting threads */ int p_cursig; /* sig which caused proc stop/exit */ ... }; Each process may also have a struct sigcount associated with it, pointed to by the field p_sigcountp in the proc structure. The sigcount structure keeps track of how many signals the process has outstanding that is, sent but not yet received. This structure is only used for signals sent with the sigqueue() system call, which allows the sender to specify data along with the signal. The kill() system call does not use the sigcount structure. There is a pool of these structures available to the system, kept on a free list. When a process sends a signal, a struct sigcount is taken from the pool and attached to the process. When the last of the signals is received, the structure is returned to the pool. The reason this is a separate structure rather than just a counter in the proc structure is that a process could send a signal, then exit before the signal is received. This gives us a persistent record of the signals sent. Listing 14.3 is the sigcount structure. Listing 14.3. sigcount structure typedef struct sigcount { unsigned int sc_count:16; /* sigqueues sent by this process and pending at a receiver */ sc_owner_state_t sc_owner_state:1; unsigned int sc_unused:7; unsigned int sc_cookie:8; /* DS ID token */ struct sigcount *sc_next; /* for freelist management */ } sigcount_t; Along with the sigcount structure, signals sent with sigqueue() also need a place to store the data that is sent along with the signal. This information must be outside of the proc structure for the same reason that the sigcount does it may have to persist after the process terminates. The structure that holds this data is a struct __ksi, or ksi_t. The more interesting fields include a pointer to the sigcount structure that's associated with the data, the number of the signal, the cause of the signal (user-generated, timer-generated, I/O completion, etc.), an errno that can be associated with the signal, and two data fields. The first of these fields, si_value, is used by the sigqueue() system call. When a user sends a signal using sigqueue(), he or she can specify a value to be sent along with the signal. The second data field, a large union called __data, contains various information depending on the signal number. Some examples are the faulting address for SIGSEGV or SIGBUS, or the status returned by a child for SIGCLD. Here's a partial list of what's in the ksi_t: typedef struct __ksi { struct sigcount *si_countp; /* sender's pending signal count */ int si_signo; /* signal number */ sigval_t si_value; int si_code; /* cause of signal */ int si_errno; /* associated errno */ union { ... } __data; } ksi_t; Each kthread structure also has some data related to signals. These are as follows: struct kthread { ... char kt_cursig; /* number of current pending signal, if any */ ksigset_t kt_sig; /* signals pending to this thread */ ksigset_t kt_ksi_avail; /* signals with siginfo available */ ksi_t *kt_ksiactive; /* list of pending queued signals*/ ksigset_t kt_sigmask; /* current signal mask */ int kt_proc_sig; /* signo of sig directed at proc */ ... } Finally, we have the ksigwait structure. Each thread that is explicitly waiting for a signal with the pause(), sigpause(), sigwait(), or sigsuspend() system calls will have one of these structures. The ksigwait structures are pointed to by the proc structure, and they in turn point to the thread that is waiting for the signal. |