signal
The signal mechanism can be understood as the soft interrupt of the process, which uses signal processing to simulate the interrupt function of the operating system
Soft interrupts are generated by executing interrupt instructions, while hard interrupts are caused by peripherals. https://zhuanlan.zhihu.com/p/85597791
The full name of the signal is soft interrupt signal, which is also called soft interrupt.
Soft interrupt signal (signal, also referred to as signal) is used to notify the process of abnormal events. Processes can send soft interrupt signals to each other through the system call kill. The kernel can also send signals to the process because of internal events to inform the process that an event has occurred. Note that signals are only used to inform a process of what happened, and do not pass any data to the process.
The execution point of signal can be understood as when returning from the kernel state to the user state. When returning, if it is found that there is a triggered signal in the process to be executed, after leaving the kernel state (that is, switching the CPU to the user mode), execute the signal processing function bound by the user process for the signal. From this point of view, The signal handler function is executed in the context of the user process. After the signal processing function is executed, it returns to the place where the user process is interrupted or interrupted by system call (soft interrupt or instruction trap).
The implementation of the signal mechanism is flexible. After the user process is interrupted or the system call falls into the kernel, the breakpoint information is saved to the stack. When the kernel returns to the user state, if there is a triggered signal, the signal processing function to be executed is directly push ed to the stack. After the CPU switches to the user mode, The signal processing function can be executed directly from the pop stack and returned to the user process. The signal handler applies the process context and simulates the soft interrupt process of the process with the actual interrupt.
If the signal is sent to a sleeping process, it depends on the priority of the process entering sleep. If the process sleeps at the priority that can be interrupted, wake up the process; Otherwise, only the corresponding bit of the signal field in the process table is set without waking up the process. This is important because the process checks whether it receives a signal: when a process is about to return from kernel state to user state, or when a process is about to enter or leave an appropriate low-key priority sleep state.
The signal received by a process is actually processed by the kernel when a process returns to the user state from the kernel state. Therefore, when a process runs in the kernel state, the soft interrupt signal does not work immediately and will not be processed until it returns to the user state
The soft interrupt signal received by the kernel processing a process is in the context of the process, so the process must be running.
1.1 signal list
In the list, the signals numbered 1 ~ 31 are the signals supported by traditional UNIX, which are unreliable signals (non real-time), and the signals numbered 32 ~ 63 are later expanded, which are called reliable signals (real-time signals). The difference between unreliable signals and reliable signals is that the former does not support queuing, which may cause signal loss, while the latter does not.
.1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1
36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5
40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5
60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1
64) SIGRTMAX
Among them, there are several common signals:
SIGINT
The program interrupt signal is sent when the user types the INTR character (usually Ctrl-C), which is used to notify the foreground process group to terminate the process.
SIGABRT
The signal generated by calling the abort function.
SIGKILL
Used to immediately end the operation of the program. This signal cannot be blocked, processed or ignored. If the administrator finds that a process cannot be terminated, he can try to send this signal.
kill -9 pid ,kill -SIGKILL
SIGPIPE
Pipe rupture. This signal is usually generated in inter process communication. For example, two processes using FIFO (pipeline) communication write to the pipeline without opening or accidental termination, and the writing process will receive SIGPIPE signal. In addition, for the two processes communicating with the Socket, when the write process writes to the Socket, the read process has terminated.
SIGTERM
Program end signal. Unlike SIGKILL, this signal can be blocked and processed. It is usually used to require the program to exit normally. The shell command kill generates this signal by default. If the process cannot be terminated, we will try SIGKILL.
kill pid,kill -15 pid ,kill -SIGTERM
SIGCHLD
When the child process ends, the parent process will receive this signal.
If the parent process does not process this signal and does not wait for the child process, although the child process terminates, it will also occupy an entry in the kernel process table. At this time, the child process is called a zombie process. We should avoid this situation (the parent process either ignores the sigshield signal, or captures it, or waits for its derived child process, or the parent process terminates first. At this time, the termination of the child process is automatically taken over by the init process).
SIGSYS
Illegal system call.
Among the signals listed above, the signals that cannot be captured, blocked or ignored by the program are: SIGKILL,SIGSTOP The signals that cannot be restored to the default action are: SIGILL,SIGTRAP By default, the signals that will cause the process to abort are: SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ The default signals that cause the process to exit are: SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM The default signals that cause the process to stop are: SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU The signals ignored by the default process are: SIGCHLD,SIGPWR,SIGURG,SIGWINCH
In addition, SIGIO exits in SVR4 and is ignored in 4.3BSD; SIGCONT continues when the process is suspended. Otherwise, it is ignored and cannot be blocked.
1.2 signal transmission
3.2.1 the signal comes from the kernel, and the request to generate the signal comes from the following three places
(1) User
The user can request the kernel to generate a signal by entering commands such as Ctrl-C, Ctrl - \ or any other key assigned to the signal control character by the terminal driver.
(2) The kernel sends a signal to the process when there is an error in the execution of the process.
For example, an illegal segment access, floating-point overflow, or an illegal instruction, the kernel also uses signals to notify the process of specific events.
(3) Process
A process can send signals to another process through the system call kill, and a process can communicate with another process through signals.
The main functions for sending signals are: kill(), raise(), sigqueue(), alarm(), setimer (), and abort().
Here is just the use of kill
#include <sys/types.h> #include <signal.h> int kill(pid_t pid,int signo)
The system call can be used to send any signal to any process or process group. The value of parameter pid is the receiving process of the signal
pid>0 process ID by pid Process of pid=0 Processes in the same process group pid<0 pid!=-1 Process group ID by -pid All processes pid=-1 All processes except the sending process itself ID Processes greater than 1
Sinno is the signal value. When it is 0 (i.e. empty signal), no signal is actually sent, but error checking is carried out as usual. Therefore, it can be used to check whether the target process exists and whether the current process has the permission to send signals to the target (processes with root permission can send signals to any process, and processes with non root permission can only send signals to processes belonging to the same session or the same user).
Kill() is most commonly used for signal transmission when PID > 0. When the call is executed successfully, the return value is 0; In case of error, return - 1 and set the corresponding error code errno. Here are some error codes that may be returned:
EINVAL: Specified signal sig Invalid. ESRCH: parameter pid The specified process or process group does not exist. Note that the process existing in the process table entry may be a process that has not been deleted wait Withdraw, but the dead process of execution has been terminated. EPERM: The process has no authority to send this signal to the process specified to receive the signal. Because a process is allowed to send signals to the process pid Must have root Power, or the name of the process that issued the call UID or EUID Associated with the specified receiving process UID Or save user ID(savedset-user-ID)Same. If parameter pid less than-1,That is, if the signal is sent to a group, the error indicates that a member process in the group cannot receive the signal.
1.3 signal acquisition and processing
The process can tell the kernel how to process the signal through the system call signal. The process has three choices.
#define SIG_DFL ((void (*) (int)) 0) * statement A* #define SIG_IGN ((void (*) (int)) 1) #define SIG_ERR ((void (*) (int)) -1) SIG_ERR(-1): In the signal processing function, failure is returned SIG_ERR. SIG_DFL(0): Default signal handler SIG_IGN(1): Ignore signal handler
(1) Receive default processing (usually die)
For example, the default processing of SIGINT is death. The process does not have to use signal to receive the default processing, but the process can restore the default processing through the following calls.
signal(SIGINT, SIG_DFL);
(2) Ignore signal
The program can tell the kernel that it needs to ignore SIGINT through the following call.
signal(SIGINT, SIG_IGN);
(3) Signal processing function
The program can tell the kernel which function to call when the program arrives.
signal(signum, functionname);
SIG_ERR: in the signal processing function, SIG is returned in case of failure_ ERR.
1.3.1 signal processing example
(1) Default processing signal
#include<stdio.h> #include<signal.h> int main() { signal(SIGINT,SIG_DFL); //If the process receives a signal, it dies int i; for( i = 0; i<10;++i) { printf("hello world\n"); sleep(1); } return 0; }
Here, we use ctrl +c to send a signal to the process. After the process receives the signal, due to the definition of SIG_DFL, so the process will die.
(2) Ignore signal
#include<stdio.h> #include<signal.h> int main() { signal(SIGINT,SIG_IGN); int i; for( i = 0; i<10;++i) { printf("hello world\n"); sleep(1); } return 0; }
3) Using signal processing functions
#include<stdio.h> #include<signal.h> int main() { void f(int); signal(SIGINT,f); int i; for( i = 0; i<10;++i) { printf("hello world\n"); sleep(1); } return 0; } void f(int signum) { printf("SIGINT\n"); }
1.4 use of sigaction function
In signals, signal is the primary usage and sigaction is the advanced usage
We have successfully sent and received signals, so why is there an advanced version? In fact, a problem with the previous signals is that although the signals are sent and received, there is always something less. Since the signals have been sent, why can't we carry some data?
That's why we need another function to carry some data through the process of signal transmission. Let's take a look at the sending function first.
#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); struct sigaction { void (*sa_handler)(int); //Signal processor, no additional data, SIG_IGN is ignored, SIG_DFL is the default action void (*sa_sigaction)(int, siginfo_t *, void *); //The signal processing program can accept additional data and use it with sigqueue sigset_t sa_mask;//For the signal set of blocking keyword, the signal can be added to the signal blocking word before calling the capture function, and the signal capture function returns to the original value before returning. int sa_flags;//Behavior affecting signals_ Siginfo indicates that data can be accepted };//Callback function handle sa_handler,sa_ Only one sigaction can be selected
The signum parameter indicates the type of signal to be captured, the act parameter specifies the new signal processing method, and the oldact parameter outputs the processing method of the previous signal (if it is not NULL).
sigaction is a system call. According to this function prototype, it is not difficult to see that in the function prototype, the first parameter signum should be the number of the registered signal; If the second parameter act is not empty, it indicates that a new configuration of the signal is required; If the third parameter oldact is not empty, the previous signal configuration can be backed up for later recovery.
sigaction is the signal receiving function, corresponding to the signal generating function sigqueue
#include <signal.h> int sigqueue(pid_t pid, int sig, const union sigval value); union sigval { int sival_int; void *sival_ptr; };
signal processing
struct sigaction act; act.sa_sigaction = handler; act.sa_flags = SA_SIGINFO; //If SA is set_ The siginfo property indicates that the processing function used is sa_sigaction, not sa_handler, otherwise, the system will use SA by default_ The signal processing function pointed to by the handler. sigaction(SIGIO, &act, NULL); //sa_sigaction and sa_handler uses the same memory space, which is equivalent to union, so only one of them can be set, not both at the same time.
give an example:
demo.c
#include<signal.h> #include<stdio.h> #include <unistd.h> void handler(int signum, siginfo_t * info, void * context){ if(signum == SIGIO) printf("SIGIO signal: %d\n", signum); else if(signum == SIGUSR1) printf("SIGUSR1 signal: %d\n", signum); else printf("error\n"); if(context) { printf("content: %d\n", info->si_int); printf("content: %d\n", info->si_value.sival_int); } } int main(void){ struct sigaction act; act.sa_sigaction = handler;//The signal processing program can accept additional data and use it with sigqueue act.sa_flags = SA_SIGINFO;//Behavior affecting signals_ Siginfo indicates that data can be accepted sigaction(SIGIO, &act, NULL); sigaction(SIGUSR1, &act, NULL); for(;;) { sleep(10000); } return 0; }
There must be several operations to complete before using this function
- If you want to get data, use the sigaction function to set SA when installing the signal handler_ Siginfo logo.
- SA in sigaction structure_ The sigaction member provides a signal capture function. If SA is implemented_ Handler member, you will not be able to get additional data.
sigqueue() passes more additional information than kill(), but sigqueue() can only send signals to one process, not to a process group. You can use the value parameter to pass an integer value or pointer value to the signal handler.
Sigqueue function can not only send additional data, but also queue signals (the operating system must realize the real-time expansion of POSIX.1). For signals with blocking settings, use sigqueue to send multiple identical signals. When unblocking, the receiver will receive the signals in the sent signal queue instead of directly receiving them once.
However, signals cannot be queued indefinitely, and the maximum value of signal queuing is subject to SIGQUEUE_MAX limit. When the maximum limit is reached, sigqueue will fail and errno will be set to EAGAIN.
send.c
#include <sys/types.h> #include <signal.h> #include<stdio.h> #include <unistd.h> int main(int argc, char** argv){ if(4 != argc) { printf("[Arguments ERROR!]\n"); printf("\tUsage:\n"); printf("\t\t%s <Target_PID> <Signal_Number> <content>\n", argv[0]); return -1; } int pid = atoi(argv[1]); int sig = atoi(argv[2]); if(pid > 0 && sig > 0) { //int sigqueue(pid_t pid, int sig, const union sigval value); union sigval val; val.sival_int = atoi(argv[3]); printf("send: %d\n", atoi(argv[3])); sigqueue(pid, sig, val); } else { printf("Target_PID or Signal_Number MUST bigger than 0!\n"); } return 0; }
reference resources https://blog.csdn.net/weixin_44933419/article/details/113368763