Linux signal Programming Practice (3) Signal Representation in the kernel (sigaction & sigqueue)
Representation of signals in the kernel
The processing action of the actually executed signal is called the Delivery, the State between the signal generation and the Delivery, and the Pending ). A process can Block a signal. When a blocked signal is generated, it remains in the pending state until the process unblocks the signal. Note that blocking and ignoring are different. As long as the signal is blocked, it will not be delivered. Ignoring is an optional action after delivery. The representation of signals in the kernel can be viewed as follows:
1) block set (blocking set and blocking set): the signal to be blocked by a process, where 1
2) pending set (pending Signal Set): If a signal is in the congestion set of the process, it is also in the corresponding position 1 of the pending set, indicating that the signal cannot be delivered and will not be processed
3) handler (signal processing function set): indicates the signal processing function corresponding to each signal. When the signal is not in the pending concentration, it will be called.
4) The block status word and pending status word are all 64-bit (bit );
5) The block status word user can read and write, and the pending status word user can only read. This is a signal design mechanism.
So how can we change and read the status of blocked characters? Next we will introduce a set of Signal Set operation functions:
# Include
Int sigemptyset (sigset_t * set); // clears the signal set; (64bit/8 = 8 bytes) int sigfillset (sigset_t * set ); // set all 64bit signal sets to 1 int sigaddset (sigset_t * set, int signo); // according to signo, convert the corresponding position of the signal set to 1 int sigdelset (sigset_t * set, int signo); // convert the corresponding position of the signal set to 0 int sigismember (const sigset_t * set, int signo); // determines whether signo is in the Signal Set
Sigprocmask function: read or change the signal Block of a process)
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
Return Value: 0 if the request is successful, and-1 if the request fails.
Read: If the oset is not a null pointer, the current signal shielding word of the read process is transmitted through the oset parameter.
Change: If set is not a null pointer, change the signal shielding word of the process. The parameter "how" indicates how to change. If both oset and set are non-null pointers, back up the original signal shielding word to oset, and then change the signal shielding word according to the set and how parameters. Assuming that the current signal shielding word is mask, the following table describes the optional values of the how parameter.
Sigpending obtains the pending information and saves it to the set state. The maximum value of the NSIG signal is 64.
#include
int sigpending(sigset_t *set);
Sigismember Function
Used to test whether the signal represented by the signum parameter has been added to the set Signal set parameter. If the signal already exists in the signal set, 1 is returned; otherwise, 0 is returned. If an error exists,-1 is returned. For details about the error and the error code, see:
The set pointer address of the EFAULT parameter cannot be accessed.
The sigval parameter signum is invalid.
int sigismember(const sigset_t *set,int signum);
Register a SIGINT signal and print the pending status. The result is as follows:
Void handler (int sig) {printf ("recv a sig = % d \ n", sig);} void printsigset (sigset_t * set) {int I; for (I = 1; I
The signal is not blocked and there is no pending status. It is delivered directly.
In the following example, we first shield the SIGINT signal, but if the process receives the SIGQUIT signal, the blocking section of the SIGINT signal will be removed. Of course, we need to register the SIGINT and SIGQUIT signals first.
/* The program that starts to block the signal and generates the pending status */void handler (int sig) {if (sig = SIGINT) printf ("recv a sig = % d \ n ", sig); else if (sig = SIGQUIT) // unblock SIGINT {sigset_t uset; sigemptyset (& uset); sigaddset (& uset, SIGINT); sigprocmask (SIG_UNBLOCK, & uset, NULL);} // printf ("recv a sig = % d \ n", sig);} void printsigset (sigset_t * set) {int I; for (I = 1; I
When we press ctrl + c to generate a signal, the signal is blocked and in the pending state. When the SIGQUIT signal is received, the blocking function is lifted and enters the delivery status, but only one response is made to the signal. Even if you press ctrl + c many times, the reason is that, the SIGINT is an unreliable signal and does not support queuing. Only one SIGINT is retained.
If we use real-time signals, such as SIGRTMIN, the signal can be queued and will not be lost. After blocking is removed, each signal will be processed.
Sigaction
We have mentioned earlier that the use of signal to install unreliable signals. Although signal is not as rich as sigaction, reliable signals can also be installed;
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Function:
The sigaction function is used to change the behavior of a process after receiving a specific signal.
In short, the parameter is (signal, pointer, original behavior)
Sigaction struct
The second parameter is the most important, including processing the specified signal, the information transmitted by the signal, and which functions should be shielded during the execution of the signal processing function.
Struct sigaction {// The signal processing program does not accept additional data (relatively outdated) void (* sa_handler) (int); // The signal processing program can accept additional data, used with sigqueue (supporting signal queuing and transmitting other information). We recommend using void (* sa_sigaction) (int, siginfo_t *, void *); sigset_t sa_mask; // block int sa_flags; // indicates the signal behavior. SA_SIGINFO indicates that the data void (* sa_restorer) (void) is acceptable. // discard the data };
The prototype of sa_handler is a function pointer with the int parameter and void type returned. The parameter is the signal value, so the signal cannot transmit any information except the signal value;
The prototype of sa_sigaction is a function pointer with three parameters: int, struct siginfo *, and void. The first parameter is the signal value. The second parameter is a pointer to the struct siginfo structure, which contains the data value carried by the signal. The third parameter is not used.
Sa_mask specifies which signals should be blocked during the execution of the signal processing program. The current signal is blocked by default.
Sa_flags contains many flag spaces. The more important one is SA_SIGINFO. When this flag is set, it indicates that parameters attached to the signal can be passed to the signal processing function. Even if sa_sigaction specifies the signal processing function, if SA_SIGINFO is not set, the signal processing function cannot obtain the data transmitted from the signal. Access to the information in the signal processing function will cause a segment error.
Sa_restorer is out of date. POSIX does not support it and should not be used any more.
Note: The callback functions sa_handler and sa_sigaction can only be selected.
Therefore, when your signal needs to receive additional information, you must assign a signal processing function pointer to sa_sigaction, and assign SA_SIGINFO to sa_flags,
Example 1: Use sigaction to implement the signal function
__sighandler_t my_signal(int sig,__sighandler_t handler){ struct sigaction act; struct sigaction oldact; act.sa_handler=handler; sigemptyset(&act.sa_mask); act.sa_flags=0; if(sigaction(sig,&act,&oldact)<0) return SIG_ERR; return oldact.sa_handler;}void handler(int sig){ printf("recv a sig=%d\n",sig);}int main(){ /* struct sigaction act; act.sa_handler=handler; sigemptyset(&act.sa_mask); act.sa_flags=0; if(sigaction(SIGINT,&act,NULL)<0) ERR_EXIT("sigaction error\n");*/ my_signal(SIGINT,handler); while(1) pause(); return 0; }
Sa_mask Option
When handler is executed, if the process receives signals contained by sa_mask, these signals will not be responded until the handler function is executed.
Sigprocmask makes it impossible to deliver even if it occurs, but sa_mask is only processing handler to shield external signals; the difference between the two is better.
Void handler (int sig) {printf ("recv a sig = % d \ n", sig); sleep (5) ;}int main () {struct sigaction act; act. sa_handler = handler; sigemptyset (& act. sa_mask); sigaddset (& act. sa_mask, SIGQUIT); // shield the SIGQUIT signal act. sa_flags = 0; if (sigaction (SIGINT, & act, NULL) <0) ERR_EXIT ("sigaction error \ n"); while (1) pause (); return 0 ;}
In response to the SIGINT signal, that is, handler processing, SIGQUIT is temporarily blocked. However, once the handler function completes processing, it immediately responds to SIGQUIT.
Siginfo_t structure:
siginfo_t{ int si_signo; /* Signal number */ int si_errno; /* An errno value */ int si_code; /* Signal code */ int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) */ pid_t si_pid; /* Sending process ID */ uid_t si_uid; /* Real user ID of sending process */ int si_status; /* Exit value or signal */ clock_t si_utime; /* User time consumed */ clock_t si_stime; /* System time consumed */ sigval_t si_value; /* Signal value */ int si_int; /* POSIX.1b signal */ void *si_ptr; /* POSIX.1b signal */ int si_overrun; /* Timer overrun count; POSIX.1b timers */ int si_timerid; /* Timer ID; POSIX.1b timers */ void *si_addr; /* Memory location which caused fault */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since Linux 2.6.32) */ }
Sigqueue
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
Function
Sigqueue is a new system call for sending signals. It is mainly used in combination with the function sigaction () for supporting signals with parameters proposed by real-time signals.
Compared with the kill function, a parameter const union sigval value (int kill (pid_t pid, int sig) is added. Therefore, sigqueue () can transmit more information than kill, however, sigqueue () can only send signals to one process, rather than to one process group.
Parameters
Parameter 1 is the process id of the received signal, and parameter 2 is used to determine the signal to be sent;
Parameter 3 is a union sigval, which specifies the signal transmission parameter, which is usually called a 4-byte value.
Note: To communicate between processes, set sa_flags to SA_SIGINFO.
Sigval Consortium
typedef union sigval{ int sival_int; void *sival_ptr; } sigval_t;
Next we will simulate an instance for inter-process communication:
Run "hello" to enable the reception, and then use "send" to send the signal. In this way, the process can see the purpose of communication.
Hello.cvoid handler(int sig,siginfo_t *info,void *ctx){ printf("recv a sig=%d data=%d\n",sig,info->si_value.sival_int); }int main(){ struct sigaction act; act.sa_sigaction=handler; sigemptyset(&act.sa_mask); act.sa_flags=SA_SIGINFO; if(sigaction(SIGINT,&act,NULL)<0) ERR_EXIT("sigaction error\n"); while(1) pause(); return 0; }Sendint main(int argc,char *argv[]){ if(argc!=2) { fprintf(stderr,"Usage %s pid\n",argv[0]); exit(EXIT_FAILURE); } pid_t pid=atoi(argv[1]); union sigval v; v.sival_int=100; sigqueue(pid,SIGINT,v); return 0;}