In signal (I), we discuss linux signal types, sources, how to install a signal, and how to operate the signal set. This section first discusses how to understand the signal from the signal life cycle, or a seemingly simple signal mechanism at a macro level (after the process receives the signal, it seems that it is no longer simple ), at the micro level, how is it implemented is also a signal for a deeper understanding. Next we also discuss some precautions for signal programming, and finally some examples of signal programming are given.
I. Signal Lifecycle
The signal is sent to the signal processing function after execution.
For a complete signal Life Cycle (after the signal is sent to the corresponding processing function for execution), it can be divided into three important stages, which are characterized by four important events: signal generation; signal registration completed in the process; signal cancellation completed in the process; signal processing function completed. The interval between two adjacent events forms a stage of the signal life cycle.
The following describes the practical significance of the four events:
- Signal "birth ". The emergence of signals refers to the occurrence of events that trigger signals (such as hardware exceptions, Timer timeouts, and call of the signal sending function kill () or sigqueue ).
- The signal is "registered" in the target process; The task_struct structure of the process contains data members with pending signals in the process:
struct sigpending pending:struct sigpending{struct sigqueue *head, **tail;sigset_t signal;}; |
The Third Member is all pending signal sets in the process. The first and second members point to the beginning and end of a sigqueue-type structure chain (called "pending signal information chain, each sigqueue structure in the information chain depicts the information carried by a specific signal and points to the next sigqueue structure:
struct sigqueue{struct sigqueue *next;siginfo_t info;} |
Signal registration means that the signal value is added to the pending signal Set of the process (sigset_t signal, the second member of the sigpending structure ), the information carried by the signal is retained to a sigqueue structure of the pending signal information chain. As long as the signal is in the pending Signal Set of the process, it indicates that the process already knows the existence of the signal, but it has not been processed yet, or the signal is blocked by the process.
Note:
When a real-time signal is sent to a process, no matter whether the signal has been registered in the process or not, it will be re-registered. Therefore, the signal will not be lost, real-time signals are also called "reliable signals ". This means that the same real-time signal can occupy multiple sigqueue structures in the pending signal information chain of the same process (each process receives a real-time signal, A structure will be allocated for it to register the signal information, and the structure will be added at the end of the pending signal chain, that is, all generated real-time signals will be registered in the target process );
When a non-real-time signal is sent to a process, if the signal has been registered in the process, the signal will be discarded, resulting in signal loss. Therefore, non-real-time signals are also called "unreliable signals ". This means that the same non-real-time signal occupies at most one sigqueue structure in the pending signal information chain of the process (after a non-real-time signal is generated, (1) if the same signal has been registered in the target structure, it will not be registered. For a process, it is equivalent to not knowing that the current signal occurs and the signal is lost; (2), if the process's pending signals do not have the same signal, register yourself in the process ).
- The logout of the signal in the process. During the execution of the target process, the system checks whether a signal is waiting for processing (this is done every time the system space is returned to the user space ). If a pending signal is waiting for processing and the signal is not blocked by the process, the process will unload the structure occupied by the signal in the pending signal chain before running the corresponding signal processing function. Whether to delete signals from pending processes is different from real-time and non-real-time signals. For non-real-time signals, because only one sigqueue structure is occupied in the pending signal information chain, after the structure is released, the signal should be deleted in the pending Signal Set (the signal is canceled). For real-time signals, multiple sigqueue structures may be occupied in the pending signal information chain, therefore, the number of sigqueue structures occupied should be treated differently: if only one sigqueue structure is occupied (the process only receives this signal once ), then the signal should be deleted from the pending signal set in the process (the signal is canceled ). Otherwise, the signal should not be deleted from the pending Signal Set of the process (the signal cancellation is completed ).
Before a process executes a signal processing function, it must first cancel the signal in the process.
- The signal life ends. After the process cancels the signal, immediately execute the corresponding signal processing function. After the execution is complete, the effect of this sending of the signal on the process is completely ended.
Note:
1) whether the signal is registered is unrelated to the function of sending the signal (such as kill () or sigqueue () and the function of signal installation (signal () and sigaction, it is only related to the signal value (signals whose signal value is smaller than sigrtmin are registered only once at most. signals whose signal value is between sigrtmin and sigrtmax are registered as long as they are received by the process ).
2) when the signal is canceled to the corresponding signal processing function, if the process receives the same signal multiple times, the real-time signal will be registered in the process each time; for non-real-time signals, no matter how many signals are received, only one signal is received and registered only once in the process.
2. Precautions for signal Programming
- Prevents Loss of signals that should not be lost. If you have a deep understanding of the signal lifecycle mentioned in section 8, it is easy to know if the signal will be lost and where it will be lost.
- Program portability
Considering the portability of the program, POSIX signal functions should be used as much as possible. POSIX signal functions are mainly divided into two types:
- POSIX 1003.1 signal functions: Kill (), sigaction (), sigaddset (), sigdelset (), sigemptyset (), sigfillset (), sigismember (), sigpending (), sigprocmask () and sigsuspend ().
- POSIX 1003.1b signal function. POSIX 1003.1b expands POSIX 1003.1 in terms of real-time signal, including the following functions: sigqueue (), sigtimedwait (), and sigwaitinfo (). Among them, sigqueue is mainly for signal sending, while sigtimedwait and sigwaitinfo () are mainly used to replace the sigsuspend () function, followed by corresponding instances.
#include <signal.h>int sigwaitinfo(sigset_t *set, siginfo_t *info). |
Similar to sigsuspend (), this function blocks a process until a specific signal occurs, but does not execute the signal processing function when the signal arrives, but returns the signal value. Therefore, in order to avoid executing the corresponding signal processing function, the process must be blocked before calling the function. Therefore, the typical code for calling this function is:
sigset_t newmask;int rcvd_sig; siginfo_t info;sigemptyset(&newmask);sigaddset(&newmask, SIGRTMIN);sigprocmask(SIG_BLOCK, &newmask, NULL);rcvd_sig = sigwaitinfo(&newmask, &info) if (rcvd_sig == -1) {..} |
If the call is successful, a signal value is returned. Otherwise,-1 is returned. The sigtimedwait () function is similar, but it increases the waiting time of a process.
- Program stability.
To enhance the stability of the program, the reentrant function should be used in the signal processing function.The reentrant (reentrant) function should be used in the signal processing program (note: the so-called reentrant function is a process that can be called by multiple tasks, when a task is called, do not worry about data errors ). After receiving the signal, the process jumps to the signal processing function and runs it. If the non-reentrant function is used in the signal processing function, the signal processing function may modify the data that should not be modified in the original process, in this way, when the process returns and then executes from the signal processing function, unexpected consequences may occur. Non-reentrant functions are considered unsafe functions in signal processing functions.
Most functions that meet the following conditions are not reentrant: (1) use static data structures, such as getlogin (), gmtime (), getgrgid (), getgrnam (), getpwuid () and getpwnam (). (2) The malloc () or free () function is called during function implementation. (3) The standard I/O function is used for implementation. The open group considers the following functions as reentrant:
_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown()、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。
Even if the signal processing functions use "security functions", you must also note that when entering the processing function, you must first save the errno value and restore the original value at the end. Because the errno value may be changed at any time during signal processing. In addition, longjmp () and siglongjmp () are not listed as reentrant functions, because other calls to the two functions are not guaranteed to be safe.
Iii. Introduction: Signal Application Example
In linux, the signal application is not as horrible as imagined. programmers have to do at most three things:
- Installation signal (sigaction () is recommended ());
- Implement three-parameter signal processing functions, handler (int signal, struct siginfo * info, void *);
- Sigqueue () is recommended for sending signals ().
In fact, for some signals, it is enough to install the signal (default or ignore the signal processing method ). Other operations may be related to the signal set.
Example 1: signal sending and Processing
Implement a signal receiving program sigreceive (where the signal is installed by sigaction ()).
#include <signal.h>#include <sys/types.h>#include <unistd.h>void new_op(int,siginfo_t*,void*);int main(int argc,char**argv){struct sigaction act;int sig;sig=atoi(argv[1]);sigemptyset(&act.sa_mask);act.sa_flags=SA_SIGINFO;act.sa_sigaction=new_op;if(sigaction(sig,&act,NULL) < 0){printf("install sigal error/n");}while(1){sleep(2);printf("wait for the signal/n");}}void new_op(int signum,siginfo_t *info,void *myact){printf("receive signal %d", signum);sleep(5);} |
Description: The command line parameter is a signal value. If sigreceive signo & is run in the background, the ID of the process can be obtained. Assume It is a pid, then run the kill-s signo pid verification signal sending and receiving and processing on another terminal. At the same time, the queuing problem of the signal can be verified.
Note:You can use sigqueue to implement a command line signal sending program sigqueuesend. SeeAppendix 1.
Example 2: Additional signal transmission information
It mainly includes two instances:
- Sends signals to the process itself and transmits pointer parameters;
# Include <signal. h> # include <sys/types. h> # include <unistd. h> void new_op (int, siginfo_t *, void *); int main (int argc, char ** argv) {struct sigaction act; union sigval mysigval; int I; int sig; pid_t pid; char data [10]; memset (data, 0, sizeof (data); for (I = 0; I <5; I ++) data [I] = '2'; mysigval. sival_ptr = data; sig = atoi (argv [1]); pid = getpid (); sigemptyset (& act. sa_mask); act. sa_sigaction = new_op; // three-parameter signal processing function act. sa_flags = SA_SIGINFO; // if (sigaction (sig, & act, NULL) <0) {printf ("install sigal error/n");} while (1) {sleep (2); printf ("wait for the signal/n"); sigqueue (pid, sig, mysigval); // sends a signal to the current process, and transmit additional information} void new_op (int signum, siginfo_t * info, void * myact) // implementation of the three-parameter signal processing function {int I; for (I = 0; I <10; I ++) {printf ("% c/n", (* (char *) (* info ). si_ptr) + I);} printf ("handle signal % d over;", signum );} |
In this example, the signal implements the transmission of additional information, and how the signal processes the information depends on the specific application.
- 2. Transmit Integer Parameters between different processes: Put the signal sending and receiving in 1 in two programs, and transmit Integer Parameters during the sending process.
Signal receiving program:
#include <signal.h>#include <sys/types.h>#include <unistd.h>void new_op(int,siginfo_t*,void*);int main(int argc,char**argv){struct sigaction act;int sig;pid_t pid;pid=getpid();sig=atoi(argv[1]);sigemptyset(&act.sa_mask);act.sa_sigaction=new_op;act.sa_flags=SA_SIGINFO;if(sigaction(sig,&act,NULL)<0){printf("install sigal error/n");}while(1){sleep(2);printf("wait for the signal/n");}}void new_op(int signum,siginfo_t *info,void *myact){printf("the int value is %d /n",info->si_int);} |
Signal sending program: The second parameter of the command line is the signal value, and the third parameter is the ID of the receiving process.
# Include <signal. h> # include <sys/time. h> # include <unistd. h> # include <sys/types. h> main (INT argc, char ** argv) {pid_t PID; int SIGNUM; Union sigval mysigval; SIGNUM = atoi (argv [1]); pid = (pid_t) atoi (argv [2]); mysigval. sival_int = 8; // it does not indicate the specific meaning. It is only used to describe the problem if (sigqueue (PID, SIGNUM, mysigval) =-1) printf ("send error/N "); sleep (2 );} |
Note:Two examples of instance 2 focus on transmitting information using signals. Currently, there are very few instances that transmit information via signals in Linux, but there are some in UNIX, however, the transfer is basically about passing an integer, and I haven't seen it yet. I have never implemented pointer transmission between different processes (in fact more meaningful). Maybe there is a problem with the implementation method. please email me.
Example 3: signal blocking and Signal Set Operations
#include "signal.h"#include "unistd.h"static void my_op(int);main(){sigset_t new_mask,old_mask,pending_mask;struct sigaction act;sigemptyset(&act.sa_mask);act.sa_flags=SA_SIGINFO;act.sa_sigaction=(void*)my_op;if(sigaction(SIGRTMIN+10,&act,NULL))printf("install signal SIGRTMIN+10 error/n");sigemptyset(&new_mask);sigaddset(&new_mask,SIGRTMIN+10);if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))printf("block signal SIGRTMIN+10 error/n");sleep(10);printf("now begin to get pending mask and unblock SIGRTMIN+10/n");if(sigpending(&pending_mask)<0)printf("get pending mask error/n");if(sigismember(&pending_mask,SIGRTMIN+10))printf("signal SIGRTMIN+10 is pending/n");if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)printf("unblock signal error/n");printf("signal unblocked/n");sleep(10);}static void my_op(int signum){printf("receive signal %d /n",signum);} |
Compile the program and run it later. Send a signal to the process on another terminal (run kill-s 42 pid, SIGRTMIN + 10 is 42). Check the running mechanism of several key functions, signal Set-related operations are relatively simple.
Note:In the above examples, the printf () function is used only as a diagnostic tool. The pringf () function cannot be reentrant and should not be used in signal processing functions.
Conclusion:
I have benefited a lot from systematically analyzing and summarizing the linux signal mechanism! Thank you for your support!
Comments and suggestions are greatly welcome!
Appendix 1:
The command line signal sending program sigqueuesend implemented by sigqueue. The second parameter of the command line is the sent signal value, and the third parameter is the process ID that receives the signal. It can be used with instance 1:
#include <signal.h>#include <sys/types.h>#include <unistd.h>int main(int argc,char**argv){pid_t pid;int sig;sig=atoi(argv[1]);pid=atoi(argv[2]);sigqueue(pid,sig,NULL);sleep(2);} |