First of all thank you Liao brother this friend to me last night wrote a "talk about the signal mechanism of Linux (a)", before the topic I used the word "analysis", give a person to analyze the feeling of the kernel. I know the skill is not enough, but can not be judged on the Linux kernel source code. After the road is still very long, I still step by step slowly walk, Linux kernel this mountain, I just arrived at the foot of the mountain.
Well, I'll go back to the writing yesterday. If there are errors, please crossing correct, thank you first.
At the end of this article, we saw a phenomenon in which the send process sent a total of 500 SIGINT signals to the RCV process, but in practice the RCV only received/processed 13 SIGINT signal processing functions (Signal-handler function). Whether the RCV process received 500 SIGINT signals only performed 13 signal processing functions, or RCV process only received 13 SIGINT signal and then executed 13 signal processing function. We cannot help but ask: where did the signal go? Before we get to the point, we need to know a bit of knowledge called signal set and signal masking.
Signal Set
When dealing with signal-related functions, we often need a special data structure to represent a set of signals, which we call a set of signals, whose data types are represented as sigset_t and are usually implemented in the form of bitmask. My environment is CentOS7, which is defined in/usr/include/bits/sigset.h, specifically as follows:
/* a ' sigset_t ' have a bit for each signal. */
# define _sigset_nwords (1024x768/(8 * sizeof (unsigned long int)))
typedef struct
{
unsigned long int __val[_sigset_nwords];
} __sigset_t;
#endif
The Sigset.h also provides a set of functions (actually implemented with macros, which are interesting to look at Sigset.h), to implement operations on sigset_t type data. The prototype is as follows:
int Sigemptyset (sigset_t *set);
int Sigfillset (sigset_t *set);
int Sigaddset (sigset_t *set, int signum);
int Sigdelset (sigset_t *set, int signum);
int Sigismember (const sigset_t *set, int signum);
In addition, GLIBC provides three additional non-standard functions:
int Sigisemptyset (const sigset_t* set);
int Sigandset (sigset_t* dest,sigset_t* left,sigset_t* right);
int Sigorset (sigset_t* dest,sigset_t* left,sigset_t* right);
After basically looking at the prototype, the usage of these functions is clear, and there is no need to waste space. In addition, I think the implementation of these functions is worth reading, is a C language in the learning of a good demo.
Signal Shielding
After understanding the basic concepts of the signal set, we can know that we continue to understand other concepts associated with the signal set, first of all the signal shielding word. It defines the set of signals to block the delivery to the current process, each of which has a signal-masking word (signal mask). If you know what is a permission mask (umask) then the signal shielding word is also well understood. The Sigprocmask () function detects and changes the signal mask Word of the current process. Its prototype:
int sigprocmask (int how, const sigset_t *set, sigset_t *oldset);
When Oldset is a non-null pointer, after calling Sigprocmask, Oldset returns to the previous signal screen word. The set parameter combines the how parameter to make changes to the current signal screen word. (as mentioned in the previous section there are two special signals that you can not block are: Sigkill and Sigstop) The specific rules are:
How |
Behavior |
Sig_block |
The signal shielding word for the set process is the set of the current signal shielding Word and set. Set is the new set of signals to be masked. |
Sig_unblock |
Sets the signal mask for the current process to be the intersection of the current signal mask and set complement , which is the current signal mask minus the set of signals to be unmasked. Set is the set of signals to unblock. |
Sig_setmask |
Sets the signal screen word for the current process to be set signal set. |
When set points to a null, however, how does not work. Usually we get the current signal mask Word by Oldset when set to null.
If one or more signals have come one or more times during the process of masking the signal, we call this signal a pending (pending) signal. Then after calling Sigprocmask () to unblock the signal, the signal is passed to the current process (SUSv3 specifies at least one signal) before Sigprocmask () returns .
The process maintains a data structure to hold the pending signals, and we can use sigpending () to obtain which signals are pending:
int sigpending (sigset_t *set);//return 0 on Success,or-1 On error
The set parameter returns the outstanding set of signals. You can then use Sigismember () to determine which signals are included in the set.
Here we can explain the problem at the end of the previous article. Because the signal () registered signal processing function on Linux is executed, the current signal is automatically added to the signal screen word of the process. When the signal processing function returns, the previous signal mask word is restored. This means that when the signal processing function executes, it does not recursively interrupt itself.
Real-time signal
Early UNIX systems defined only 32 of the signals. POSIX.1B defines an additional set of real-time signals (in order to be compatible with previous applications, rather than modifying previous traditional signals). The characteristics of the real-time signal, "Linux system Programming Manual" has a summary of a very comprehensive:
- Realtime signals provide an increased range of signals the can is used for application-defined purposes. Only the standard signals is freely available for application-defined PURPOSES:SIGUSR1 and SIGUSR2.
- Realtime signals is queued. If multiple instances of a realtime signal be sent to a process, then the signal is delivered multiple times. By contrast, if we send further instances of a standard signal that's already pending for a process, which signal is D Elivered only once.
- < EM id= "__mcedel" >when sending a realtime signal, it's possible to specify data (an integer or pointer value) that accompanies the signal. The signal handler in the receiving process can retrieve this data.
- The order of delivery of different realtime signals is guaranteed. If multiple differentRealtime signals was pending, then the lowest-numbered signal was deliveredFirst . In other words, signals is prioritized, with lower-numbered signalshave higher priority. When multiple signals of the same type is queued,They is Delivere-along with their accompanying data-in the order in whichthey were sent.
According to 2nd, we can change the SIGINT at the end of the previous blog to Sigrtmin+5 (of course, as long as the real-time signal,Linux Kill () can also send real-time signals ), and then repeat yesterday's test, we will be pleasantly surprised to find that The RCV process received and processed 500 signal processing functions "without exception".
So how do you pass data when you send a real-time signal? Don't worry, you have to master a system call Sigaction ().
Sigaction () system call
We've lifted the signal () function before, sigaction () is another option, it's more powerful and more compatible, and at any time we should prioritize using sigaction (), even if signal () is simpler and more flexible. Its function prototype:
int sigaction (int signum, const struct sigaction *act,struct sigaction *oldact);//return 0 on Success,or-1 On error
Similarly to Sigprocmask, OLDACT returns the signal settings before the act is used to set up new signal processing. Signum Nature does not have to explain, this is to deal with the signal. The key to this function is that struct sigaction this struct with the same name as the function. Of course, to use sigaction () or to start with the struct sigaction, it is defined as:
struct Sigaction {
Union {
void (*sa_handler) (int);
void (*sa_sigaction) (int, siginfo_t *, void *);
}__sigaction_handler; &NBSP;//AD Dress of handler
sigset_t sa_mask //signals blocked during the handler invocation
&N Bsp int sa_flags; , &NB Sp //flags controlling handler invocation
void (*sa_restorer) (void ); //restore,not use
};
A sa_mask is a set of signals that is added to the signal-mask word of the process until the signal-processing function returns, before the signal-processing function is called. Using the Sa_mask parameter, we can specify a set of signals so that our signal processing functions are not interrupted by these signals. As with the previous signal (), the signal that raises the signal processing function is automatically added to the signal screen word of the process by default. sa_flags parameters, if you have experience, it is not difficult to guess that this must be a set of options, after all, battle-hardened. So let's see what this group of options means:
Sa_flags |
Description |
Sa_interrupt |
System calls that are interrupted by this signal do not restart automatically. |
Sa_nocldstop |
When the Signum is SIGCHLD, this signal will not be generated when a child process that accepts a signal is stopped or resumed (a bit around). However, this signal is still generated when the child process terminates. (If Sig is SIGCHLD, and don ' t generate this signal when a child process is stopped or resumed as a Consequenc E of receiving a signal.) |
Sa_nocldwait |
When Signum is SIGCHLD, the child process is not converted to a zombie process when it terminates. When wait () is called, blocking to all child processes terminates before returning -1,errno is considered as echild. |
Sa_nodefer |
When the signal is captured, the signal is not automatically added to the signal screen word of the process before the signal processing function is executed. |
Sa_onstack |
When the signal handler function is called, the standby stack is installed using the Sigaltstack (). |
Sa_resethand |
When this signal is captured, the signal handler is set to the default value SIG_DFL before the signal processing function is called, and the SA_SIGINFO flag is cleared. |
Sa_restart |
System calls that are interrupted by this signal are automatically restarted. |
Sa_siginfo |
The signal processing function is called with additional data to be processed, see below. |
The Sa_restorer is the same as the name reserved for the parameter and is not required for use. The last we want to look at is __sigaction_handler, which is a consortium (of course, this is nonsense). Both Sa_handler and sa_sigaction are pointers to signal processing functions, so only one of them can be selected at a time. If the Sa_siginfo bit is set in Sa_mask then the signal handler function is called in the form of Void (*sa_sigaction) (int, siginfo_t *, void *), otherwise void (*sa_handler) is used (int ) such a function. Let's take a look at the Sa_sigaction function:
void sa_sigaction (int signum, siginfo_t* info, void* context);
Siginfo_t is a struct whose structure and implementation are related, as is the case on my CentOS7 system:
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 (is 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) */
}
The meaning of each field is followed by a clear comment, but there is also a parameter that we need to pay special attention to, where the Si_value field is used to receive the data that accompanies the signal, whose type is a sigval_t union, defined (my system is in the path/usr/include/ On bits/siginfo.h):
# define __have_sigval_t 1
/* Type for data associated with a signal. */
typedef Union SIGVAL
{
int sival_int;
void* sival_ptr;
} sigval_t;
#endif
In actual programming, whether the Sival_int or Sival_ptr field is selected or not depends on your application. But since pointers can only be scoped to the inside of a process, sending a pointer to another process generally has little practical meaning.
Basically written here, we can use sigaction () to signal processing of the demo, but here we do not hurry to write, save to the next section to write.
Using Sigqueue ()
Before we mentioned that when sending real-time signals can be accompanied by data, kill (), raise () and other functions of the parameters are doomed they can not be accompanied by more data, here we want to know a new function sigqueue () is specifically used to send the signal when the additional delivery of additional data.
int Sigqueue (pid_t pid, int sig, Const Union Sigval value);//return 0 on success, or-1 on Error
The first two parameters are the same as kill (), but unlike Kill (), it is not possible to use PID only as a single process, rather than as a rich usage of kill (). The type of value is the sigval_t mentioned above, and it is clear that the value sent here by the sending process can be obtained in the receiving process through the siginfo_t info parameter in the signal processing function sa_sigaction.
A simple demo for processing real-time signal signals, processing the signal-side code CATCH.C:
#include <signal.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include < Stdlib.h>void sighandler (int sig,siginfo_t* info,void* context) { printf ("Send process PID =%ld,receive a data:%d \ n ", info->si_pid,info->si_value.sival_int);} int main () { printf ("pid =%ld\n", (long) getpid ()); struct Sigaction Act; Act.sa_flags = Sa_siginfo; Sigemptyset (&act.sa_mask); Act.sa_sigaction = Sighandler; if (sigaction (sigrtmin+5,&act,0) = =-1) exit ( -1); Pause ();}
Send Signal End SEND.C:
#include <unistd.h> #include <stdio.h> #include <signal.h> #include <string.h>int main (int argc , char* argv[]) { printf ("Send Process pid =%ld\n", (long) getpid ()); Union Sigval value; Value.sival_int = 5435620; pid_t pid = (pid_t) atol (argv[1]); Sigqueue (Pid,sigrtmin+5,value);}
Running the result, the process ID of the sending process and the transmitted data were successfully obtained in sa_sigaction:
Of course, because of the late night, this demo is still relatively simple, basically we use has no obstacles.
Ready to put the knowledge of the signal is summed up, a write out, only to find that the signal this part of the knowledge point is really many, and involved in a lot of details of things, it seems that this task is not finished tonight, continue tomorrow.
If you find my blog has the wrong place, please correct me, I thank you here first! contact email [email protected].
On the signal mechanism in Linux (II.)