Linux signal signal processing mechanism

Reprinted from: http://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html

 

Signal is a very important part of Linux programming. This article will introduce in detail the basic concept of signal mechanism, the general implementation method of signal mechanism in Linux, how to use signal, and several system calls related to signal.

The signal mechanism is a method for transmitting messages between processes. The signal is called a soft interrupt signal, and some people call it a soft interrupt. As can be seen from its name, its essence and use are very similar to interrupts. Therefore, signals can be said to be part of process control.

1. Basic Concepts of Signals

This section first introduces some basic concepts of signals, and then gives some basic signal types and events corresponding to signals. The basic concepts are particularly important for understanding and using signals, and for understanding the mechanism of signals. Let's take a look at what a signal is.

1. Basic concepts

Soft interrupt signal (signal, also referred to as signal) is used to notify the process that an asynchronous event has occurred. Processes can send soft interrupt signals to each other through the system call kill. The kernel can also send signals to the process due to internal events, notifying the process that an event has occurred. Note that signals are only used to notify a process of what happened, and do not pass any data to the process.

The process that receives the signal handles the various signals differently. The processing methods can be divided into three categories: the first is an interrupt-like handler. For the signal that needs to be processed, the process can specify a processing function, and the function can handle it. The second method is to ignore a signal and do nothing with it as if it never happened. The third method is to keep the default value of the system for the processing of the signal. This default operation, the default operation for most signals, is to cause the process to terminate. The process uses the system call signal to specify the processing behavior of the process to a signal.

There is a soft interrupt signal field in the entry of the process table. Each bit in this field corresponds to a signal. When a signal is sent to the process, the corresponding bit is set. It can be seen from this that the process can reserve different signals at the same time, but for the same signal, the process does not know how many have come before processing.

2. Types of signals There are many reasons for

sending signals. Here are simple classifications according to the reasons for sending signals to understand various signals:

(1) Signals related to process termination. This type of signal is emitted when the process exits, or when the child process terminates.
(2) Signals related to process exception events. Such as the process out of bounds, or an attempt to write a read-only memory area (such as the program text area), or the execution of a privileged instruction and various other hardware errors.
(3) Signals associated with encountering an unrecoverable condition during a system call. For example, when the system call exec is executed, the original resources have been released, and the current system resources have been exhausted.
(4) Signals related to unpredicted error conditions encountered while executing a system call. Such as executing a system call that does not exist.
(5) A signal sent by a process in user mode. For example, a process calls the system call kill to send signals to other processes.
(6) Signals related to terminal interaction. Such as the user closes a terminal, or presses the break key and so on.
(7) Signals that track the execution of the process.

The list of signals supported by Linux is as follows. Many of the signals are architecture-dependent, starting with the signals listed in POSIX.1:

Signal Value Handling Actions Signal Reasons
----------------- -------------------------------------------------- ---
SIGHUP 1 A Terminal hangs or the controlling process terminates
SIGINT 2 A Keyboard interrupt (eg break key is pressed)
SIGQUIT 3 C Keyboard exit key is pressed
SIGILL 4 C Illegal instruction
SIGABRT 6 C Issued by abort(3) Exit instruction
SIGFPE 8 C floating point exception
SIGKILL 9 AEF Kill signal
SIGSEGV 11 C Invalid memory reference
SIGPIPE 13 A Broken pipe: write to a pipe with no read port
SIGALRM 14 A Signal SIGTERM issued by alarm(2)
15 A Termination signal
SIGUSR1 30,10,16 A User-defined signal 1
SIGUSR2 31 ,12,17 A User-defined signal 2
SIGCHLD 20,17,18 B Child process end signal
SIGCONT 19,18,25 Process continues (the process that was stopped)
SIGSTOP 17,19,23 DEF Terminate process
SIGTSTP 18,20, 24 D Stop key pressed on the controlling terminal (tty)
SIGTTIN 21,21,26 D Background process attempts to read
SIGTTOU from the controlling terminal 22,22,27 D Background process attempts to write from the controlling terminal

The following signals are not listed in POSIX.1 out, and listed in SUSv2

Signal value processing action signaled reasons
----------------------------------- ---------------------------------
SIGBUS 10,7,10 C Bus error (bad memory access)
SIGPOLL A Pollable event defined by Sys V, synonymous with SIGIO
SIGPROF 27,27,29 A Profiling timer to
SIGSYS 12,-,12 C Invalid system call (SVID)
SIGTRAP 5 C Trace/breakpoint capture
SIGURG 16,23,21 B Socket panic (4.2 BSD)
SIGVTALRM 26,26,28 A Real time alarm clock signal (4.2 BSD)
SIGXCPU 24,24,30 C Exceeded CPU time limit (4.2 BSD)
SIGXFSZ 25,25,31 C Exceeded set file size limit (4.2 BSD)

(For SIGSYS, SIGXCPU, SIGXFSZ, and SIGBUS under some machine architectures, the default action of Linux is A (terminate), SUSv2 is C (terminate and dump core)).

Here are some other signals

Signal value processing actions are signaled for reasons
------------------------------------ ----------------------------------
SIGIOT 6 C IO capture instruction, synonymous with SIGABRT
SIGEMT 7,-, 7
SIGSTKFLT -,16,- A Coprocessor stack error
SIGIO 23,29,22 A An I/O operation can now be performed (4.2 BSD)
SIGCLD -,-,18 A Synonymous with SIGCHLD
SIGPWR 29,30,19 A Power failure (System V)
SIGINFO 29,-,- A Synonymous with SIGPWR
SIGLOST -,-,- A File lock lost
SIGWINCH 28,28,20 B Window size changed (4.3 BSD, Sun)
SIGUNUSED -,31,- A Unused signal (will be SIGSYS)

(here, - means the signal is not implemented; three values ​​are given with the meaning of , the first value is usually valid on Alpha and Sparc, the middle value corresponds to i386 and ppc and sh, and the last value corresponds to mips. Signal 29 is SIGINFO/SIGPWR on Alpha and SIGLOST on Sparc.)

Process action one The meanings of the letters are as follows:
A The default action is to terminate the process
B The default action is to ignore this signal
C The default action is to terminate the process and perform a kernel image dump (dump core)
D The default action is to stop the process
E signal Cannot be trapped
F Signal cannot be

ignored The signals described above are supported by common systems. The names, functions and default processing actions of various signals are introduced in the form of a table. The meanings of various default processing actions are: terminating the program means that the process exits; ignoring the signal means discarding the signal and not processing it; stopping the program means that the program suspends and can be resumed after entering the stopped state, usually in the During debugging (such as the ptrace system call); kernel image dumping refers to dumping the image of the process data in memory and the part of the content stored in the kernel structure of the process to the file system in a certain format, and the process exits execution. The benefit is that it provides programmers with the convenience of getting the data values ​​at the time the process was executing, allowing them to determine the cause of the dump, and to debug their programs.

Note that the signals SIGKILL and SIGSTOP can neither be caught nor ignored. The signals SIGIOT and SIGABRT are one signal. It can be seen that the same signal may have different values ​​in different systems, so it is recommended to use the name defined for the signal instead of directly using the value of the signal.

Second, the signal

mechanism The basic concept of the signal was introduced in the previous section. In this section, we will introduce how the kernel implements the signal mechanism. That is, how the kernel sends a signal to a process, how the process receives a signal, how the process controls its own response to the signal, when the kernel processes and how to process the signal received by the process. Also introduce the role of setjmp and longjmp in the signal.

1. The basic processing method

of the kernel to send a soft interrupt signal to a process is to set the bit corresponding to the signal in the signal field of the process table entry where the process is located. It should be added here that if the signal is sent to a sleeping process, it depends on the priority of the process entering sleep. If the process sleeps at an interruptible priority, the process is woken up; otherwise, only the signal in the process table is set. field corresponding bits without waking up the process. This is important because the timing for a process to check for a signal is when a process is about to return from kernel mode to user mode; or when a process is about to enter or leave an appropriate low scheduling priority sleep state.

The time when the kernel handles a signal received by a process is when a process returns from kernel mode to user mode. Therefore, when a process is running in kernel mode, the soft interrupt signal does not work immediately, and will not be processed until it returns to user mode. The process will return to user mode only after processing the signal, and the process will not have unprocessed signals in user mode.

The kernel handles a softirq signal received by a process in the context of the process, so the process must be running. When I introduced the concept earlier, I mentioned that there are three types of signal processing: the process exits after receiving the signal; the process ignores the signal; after the process receives the signal, it executes the function that the user sets to use the system to call the signal. When a process receives a signal that it ignores, the process discards the signal and continues to run as if it had not received the signal. If the process receives a signal to be caught, the user-defined function is executed when the process returns from kernel mode to user mode. And the method of executing user-defined functions is very clever. The kernel creates a new layer on the user stack. In this layer, the value of the return address is set to the address of the user-defined processing function, so that when the process returns from the kernel and pops the top of the stack It returns to the user-defined function, and when returning from the function and popping the top of the stack, it returns to the place where it originally entered the kernel. The reason for this is that user-defined handler functions cannot and are not allowed to execute in kernel mode (if the user-defined function runs in kernel mode, the user can gain any privileges).

There are several points in the signal processing method that deserve special attention. First, in some systems, before a process returns to user mode after processing the interrupt signal, the kernel clears the address of the processing routine for the signal set in the user area, that is, the next time the process handles the signal and changes it again. The default value, unless the signal system call is used again before the next signal arrives. This may cause the process to get the signal again before calling signal and cause it to exit. In BSD, the kernel no longer clears this address. But not clearing the address may cause the process to get a signal too quickly and cause a stack overflow. To avoid the above situation. In the BSD system, the kernel simulates the processing method for hardware interrupts, that is, when processing an interrupt, it prevents receiving new interrupts of this type.

The second thing to pay attention to is that if the signal to be caught occurs when the process is in a system call, and the process sleeps at an interruptible priority, then the signal causes the process to do a longjmp and jump out of the sleep state. Return to userland and execute the signal handling routine. When returning from a signal-handling routine, the process behaves as if it were returning from a system call, but returns an error code indicating that the system call was interrupted. It should be noted that in BSD systems the kernel can automatically restart system calls.

The third point to note: if the process sleeps at an interruptible priority, when it receives a signal to be ignored, the process is woken up, but does not do longjmp, and generally continues to sleep. But the user does not feel that the process has been awakened, but as if the signal has not occurred.

The fourth point to note: The kernel handles the child process termination (SIGCLD) signal differently from other signals. When a process detects that it has received a signal to terminate the child process, by default, the process acts as if it did not receive the signal. If the parent process executes the system call wait, the process will wake up from the system call wait and Return to the wait call, perform a series of subsequent operations to the wait call (find the dead child process, release the child process's process table entry), and return from wait. The purpose of the SIGCLD signal is to wake up a process sleeping at an interruptible priority level. If the process catches this signal, it goes to the handler routine as normal signal handling. If the process ignores the signal, then the action of the system call wait is different, because the function of SIGCLD is only to wake up a process sleeping on an interruptible priority, then the parent process that executes the wait call is woken up and continues to execute the wait call. Subsequent operations, then wait for other child processes.

If a process calls the signal system call and sets the SIGCLD processing method, and the process has a child process that is in a zombie state, the kernel will send a SIGCLD signal to the process.

2. The role of setjmp and longjmp In the

previous introduction to the signal processing mechanism, setjmp and longjmp were mentioned many times, but their functions and implementation methods were not explained in detail. Here is a brief introduction to this.

When introducing signals, we saw several places that require the process to return directly from the original system call after checking for a signal, rather than waiting for the call to complete. This situation where a process suddenly changes its context is the result of using setjmp and longjmp. setjmp stores the saved context in userland and continues to execute in the old context. That is to say, the process executes a system call. When going to sleep for resource or other reasons, the kernel makes a setjmp for the process. If it is woken up by a signal during sleep and the process can no longer go to sleep, the kernel calls longjmp for the process. This operation is that the kernel restores the context of the original setjmp call saved in the process user area to the current context for the process, so that the process can restore the state before waiting for the resource, and the kernel returns 1 for setjmp, so that the process knows that the system call failed. . That's what they do.

3. System calls about signals The

previous two sections have introduced most of the knowledge about signals. In this section we will look at these system calls. Among them, the system call signal is the processing method used by the process to set a certain signal, and the system call kill is used to send the signal to the specified process. These two calls can form the basic operation of the signal. The last two calls to pause and alarm are the process pause and timer implemented by signals, and the call to alarm is to notify the process that the timer expires by means of signals. So here, we also introduce these two calls.

1. The signal system call The

system call signal is used to set the processing method of a signal. The format of the call declaration is as follows:
void (*signal(int signum, void (*handler)(int)))(int);
Include the following header file in the process using the call:
#include <signal.h>

The above declaration The format is more complicated. If you don't know how to use it, you can also use the format defined by the following type (defined by POSIX):
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
But this format has different type definitions in different systems, so to use this format, it is best to refer to the online manual.

In the call, the parameter signum indicates the signal for which the handler method is to be set. The second parameter handler is a handler function, or
SIG_IGN: ignore the signal pointed to by the parameter signum.
SIG_DFL: Restore the processing method of the signal pointed to by the parameter signum to the default value.

The integer parameter passed to a signal-handling routine is the signal value, which allows one signal-handling routine to handle multiple signals. The return value of the system call signal is the previous processing routine of the specified signal signum or the error code SIG_ERR returned when an error occurs. Let's look at a simple example:

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void sigroutine(int dunno) { /* signal processing routine, where dunno will get the signal's value*/
switch (dunno) {
case 1:
printf("Get a signal -- SIGHUP ");
break;
case 2:
printf("Get a signal -- SIGINT ");
break;
case 3:
printf("Get a signal -- SIGQUIT ");
break;
}
return;
}

int main() {
printf("process id is %d ",getpid());
signal(SIGHUP, sigroutine); //* the following settings Three signal processing methods
signal(SIGINT, sigroutine);
signal(SIGQUIT, sigroutine);
for (;;) ;
}

The signal SIGINT is issued by pressing Ctrl-C, and the signal SIGQUIT is issued by pressing Ctrl-. The result of the program execution is as follows:

localhost:~$ ./sig_test
process id is 463
Get a signal -SIGINT //The result obtained by pressing Ctrl-C
Get a signal -SIGQUIT //The result obtained by pressing Ctrl-
//Press Press Ctrl-z to put the process in the background
[1]+ Stopped ./sig_test
localhost:~$ bg
[1]+ ./sig_test &
localhost:~$ kill -HUP 463 //Send a SIGHUP signal to the process
localhost:~$ Get a signal – SIGHUP
kill -9 463 //Send a SIGKILL signal to the process to terminate the process
localhost:~$

2, kill system call The

system call kill is used to send a signal to the process. The format of the call declaration is as follows:
int kill(pid_t pid, int sig);
Add the following header file to the process using the call:
#include <sys/types.h>
#include <signal.h>

The system call can be used with to send any signal to any process or process group. If the argument pid is a positive number, the call sends the signal sig to the process with process id pid. If pid is equal to 0, then the signal sig will be sent to all processes in the process group to which the current process belongs. If the parameter pid is equal to -1, the signal sig will be sent to all processes except process 1 and itself. If the parameter pid is less than -1, the signal sig will be sent to all processes belonging to the process group -pid. If the parameter sig is 0, no signal will be sent. When the call is executed successfully, the return value is 0; when there is an error, -1 is returned, and the corresponding error code errno is set. The following are some error codes that may be returned:
EINVAL: The specified signal sig is invalid.
ESRCH: The process or process group specified by the parameter pid does not exist. Note that the process that exists in the process table entry may be a zombie process that has not been reclaimed by wait, but has terminated execution.
EPERM: The process has no right to send this signal to the process designated to receive the signal. Because a process is allowed to send a signal to the process pid, it must have root authority, or the UID or EUID of the calling process is the same as the UID or saved user ID (savedset-user-ID) of the specified receiving process. If the parameter pid is less than -1, that is, the signal is sent to a group, the error indicates that there are member processes in the group that cannot receive the signal.

3. The pause system call The function of the

system call pause is to wait for a signal. The declaration format for this call is as follows:
int pause(void);
Add the following header file to the process using the call:
#include <unistd.h>

This call puts the calling process to sleep until a signal is received. This call always returns -1 and sets the error code to EINTR (a signal was received). Here is a simple example:

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void sigroutine(int unused) {
printf("Catch a signal SIGINT ");
}

int main() {
signal(SIGINT, sigroutine);
pause();
printf("receive a signal ");
}

In this example, the program starts executing, as if it entered an infinite loop, because the process is waiting for a signal, when we press Ctrl-C, the signal is caught, and pause exits the waiting state.

4. Alarm and setitimer system calls The function of the

system call alarm is to set a timer. When the timer counts up, it will send a signal to the process. The declaration format of the call is as follows:
unsigned int alarm(unsigned int seconds);
Add the following header file to the process using the call:
#include <unistd.h> The

system call alarm arranges for the kernel to issue the specified seconds for the calling process A SIGALRM signal. If the specified parameter seconds is 0, no more SIGALRM signals are sent. The next setting will cancel the previous setting. The return value of this call is the time remaining between the last scheduled call and the send, or 0 because there is no previous scheduled call.

Note that when using, the alarm is only set to send a signal once, if you want to send it multiple times, you need to use the alarm call multiple times.

For alarm, no examples are given here. Many programs in the current system no longer use the alarm call, but use the setitimer call to set the timer, and use the getitimer to get the status of the timer. The declaration format of these two calls is as follows:
int getitimer(int which, struct itimerval *value );
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
Add the following header file to the process using these two calls:
#include <sys/time.h>

This system call provides three timers to the process, each of which has its own timing domain. When any one of them arrives, it sends a corresponding signal to the process and makes the timer restart. The three timers are specified by the parameter which, as follows:
TIMER_REAL: Timing according to the actual time, the process will be sent a SIGALRM signal when the timing is reached.
ITIMER_VIRTUAL: Timing is only done when the process is executing. When the timer expires, the SIGVTALRM signal is sent to the process.
ITIMER_PROF: Time both when the process executes and when the system performs actions for the process. A pair with ITIMER_VIR-TUAL, this timer is often used to count the time the process spends in user mode and kernel mode. When the timer expires, a SIGPROF signal is sent to the process.

The parameter value in the timer is used to indicate the time of the timer. Its structure is as follows:
struct itimerval {
struct timeval it_interval; /* the next value*/
struct timeval it_value; /* the current setting value*/
};

The timeval structure in this structure is defined as follows:
struct timeval {
long tv_sec; /* seconds*/
long tv_usec; ​​/* microseconds, 1 second = 1000000 microseconds*/
};

In the setitimer call, if the parameter ovalue is not empty, the value set by the last call is retained. When the timer decrements it_value to 0, a signal is generated, and the value of it_value is set to the value of it_interval, and then the timing is restarted, and so on. The timer stops when it_value is set to 0, or when it expires and it_interval is 0. When the call is successful, it returns 0; when there is an error, it returns -1, and sets the corresponding error code errno:
EFAULT: The parameter value or ovalue is an invalid pointer.
EINVAL: The parameter which is not one of ITIMER_REAL, ITIMER_VIRT, or ITIMER_PROF.

Here's a simple demonstration of a setitimer call that emits a SIGALRM every one second and a SIGVTALRM every 0.5 seconds:

#include <signal.h>
#include <unistd.h>
#include <stdio .h>
#include <sys/time.h>
int sec;

void sigroutine(int signo) {
switch (signo) {
case SIGALRM:
printf("Catch a signal -- SIGALRM ");
break;
case SIGVTALRM:
printf(" Catch a signal -- SIGVTALRM ");

}
return;
}

int main() {
struct itimerval value,ovalue,value2;
sec = 5;

printf("process id is %d ",getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);

value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);

value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);

for (;;) ;
}

该例子的屏幕拷贝如下:

localhost:~$ ./timer_test
process id is 579
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal –GVTALRM

This article briefly introduces the signals under Linux, Consult the online manual or other documentation if you wish to learn about other invocations.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325385648&siteId=291194637