Programming signals in Linux

Source: Internet
Author: User
Tags sigint signal sleep function

Signal Concept

The signal is software interruption. It can be used as a mechanism for inter-process communication. More importantly, the signal always interrupts the normal operation of a process and is more used to handle abnormal situations. The signal is asynchronous and the process does not know when the signal will arrive. Processes can process signals or send signals to specific processes. Each signal has a name that starts with sig. For example, SIGABRT indicates the abnormal termination signal of a process.

 

Signal Source

Hardware exceptions generate signals such as Division 0 and invalid storage access. These conditions are usually detected by hardware and notified to the kernel. Then the kernel generates an appropriate signal for the process that is running when this condition occurs. The software generates abnormal signals, which can be generated using kill, raise, alarm, setimer, and sigqueue.

 

Signal type

Unreliable signal: Linux signal mechanism is basically inherited from UNIX systems. In early Unix systems, the signal mechanism was relatively simple and original, and some problems were exposed in practice. Therefore, the signals built on the early mechanism were called "unreliable signals ", an unreliable signal whose signal value is smaller than sigrtmin. After each signal is processed, the corresponding processing function of the signal is restored to the default value.
The signal may be lost.

Reliable signal: the signal between sigrtmin and sigrtmax is a reliable signal, which overcomes the possibility of signal loss. Real-time and non-real-time signals: Linux currently defines 64 types of signals (which may be extended in the future), the first 32 types of non-real-time signals, and the last 32 types of real-time signals. Non-real-time signals do not support queuing. They are both unreliable signals and real-time signals support queuing. They are all reliable signals.

 

Linux System Signal

There are many signals in Linux. For more information, see advanced programming in UNIX environment P235.

 

Signal processing process: See, we can see the interaction between process 1 and the kernel.

 

Signal terminology

Signal Generation: When a signal event occurs, it generates a signal (or sends a signal to a process) for the process ). When a signal is generated, the kernel sets an identifier in the input table. Delivery of signals (delivery): it is called delivery when a process acts on a signal (executes a signal processing function or ignores it. During the interval between signal generation and delivery, the signal is called pending ). Block: A process can specify delivery blocking for a signal. If the signal is processed by default or captured at this time, the signal will be in the final State until the process unblocks the signal delivery or changes the processing method to ignore. If the signal is processed by ignoring the signal, the signal will never be in the delivery or delivery pending status.

 

Signal usage

The process can use signals in three ways: Specify the processing function (Signal Processing) of the process when the signal is received ). Blocking a signal (that is, delaying its occurrence). For example, if it is in a critical code segment, it is enabled after the critical code is executed. Sends a signal to another process.

 

Signal Processing

Ignore this signal: Most signals can be processed in this way, but two signals cannot be ignored. They are: sigkill and sigstop. When these two signals cannot be ignored: they provide super users with a reliable method to terminate or stop processes; capture signals: to do this, we need to notify the kernel to call a user function when a signal occurs. In user functions, the executable user wants to process such events. Execute the default system action: the default action for most signals is to terminate the process.

 

Signal registration function: Signal

The signal function is used to notify the kernel how to process a specific signal (ignore, capture, and handle by default ).
# Include <signal. h> <br/> typedef void (* sighandler_t) (INT); <br/> sighandler_t signal (int signum, sighandler_t handler );
The SIGNUM parameter is the signal name. You can use kill-L to list all signals (represented by integers ).
The handler parameter is the signal processing method. A) sig_ign ignores this signal, B) sig_dfl processes this signal by default, c) User-Defined Function address, the Linux kernel calls this function when the signal is delivered.

Failed: returns sig_err, succeeded: returns the previously registered signal processing function.
# Define sig_err (void (*) ()-1 <br/> # define sig_dfl (void (*)()) 0 <br/> # define sig_ign (void (*) () 1

 

Signal wait function: Pause

Pause changes the calling process to sleep until a signal is sent. If you use sig_ign for a signal, the pause will not be awakened.# Include <unistd. h> <br/> int pause (void );
The pause return value is-1, and errno is set to eintr.

 

Special Signal

Sigkill and sigstop cannot be ignored or captured. They can only be processed by default.
Sigkill the process that receives the signal will terminate immediately.
Sigstop is used to stop a process.
Let the kernel and super users have a way to determine whether to kill or stop a process. The following code is the Demo code of sig_kill. The main function registers the sig_kill signal to the kill_stop_handler function.

# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> </P> <p> void kill_stop_handler (INT num) <br/> {<br/> printf ("in SIGINT handler/N "); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> If (sig_err = signal (sigkill, kill_stop_handler) {<br/> printf ("registr sigkill handler failed! /N "); <br/> return-1; <br/>}< br/> pause (); </P> <p> return 0; <br/>}

 

Subprocess Signal Processing

The child process inherits the signal processing method of the parent process until the child process calls the exec function. After the child process calls the exec function, exec changes the signal set as captured in the parent process to the default processing method, and the rest remain unchanged. For example, in the parent process, set sigterm to capture and SIGINT to ignore. The sub-process executes exec.# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> </P> <p> void term_handler (INT num) <br/>{< br/> printf ("in sigterm handler % d/N ", getpid (); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> pid_t PID; <br/> If (sig_err = signal (sigterm, term_handler) {<br/> printf ("registr sigterm Handler Failed! /N "); <br/> return-1; <br/>}</P> <p> If (sig_err = signal (SIGINT, sig_ign )) {<br/> printf ("registr SIGINT ignore handler failed! /N "); <br/> return-1; <br/>}< br/> pid = fork (); <br/> If (PID <0) {<br/> printf ("fork error! /N "); <br/> return-1; <br/>}< br/> If (0 = PID) {<br/> execv (" term_int ", argv); <br/> printf ("Never go here/N"); <br/> return-1; <br/>}< br/> pause (); </P> <p> return 0; <br/>}

The code for term_int.c is as follows:

# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> </P> <p> int main (INT argc, char ** argv) <br/> {<br/> pause (); <br/> return 0; <br/>}

 

Signal Set

Currently, the Linux system defines 64 signals, which may be further expanded in the future. Therefore, the type sigset_t is used to represent all signals. Generally, a bit in sigset_t represents a signal. Special operation functions are required for Signal Set Operations. Signal 0 is retained by the system and is not used.

The following five Signal Set operation functions are available:
# Include <signal. h>
Set the bits of all signal sets to 0 int sigemptyset (sigset_t * Set );
Set the bits of all signal sets to 1 int sigfillset (sigset_t * Set );
Add the signal SIGNUM to the set int sigaddset (sigset_t * Set, int SIGNUM );
Clear int sigdelset (sigset_t * Set, int SIGNUM) from the signal set );
Determine whether a signal is in the signal set int sigismember (const sigset_t * Set, int SIGNUM );

 

Signal registration function: sigaction

In Linux, the signal registration function is ultimately implemented by calling sigaction, and retaining signal is mainly for compatibility.
# Include <signal. h> <br/> int sigaction (int signum, const struct sigaction * Act, struct sigaction * oldact); <br/>
The first SIGNUM specifies the signal to be processed (except sigkill and sigstop ). The second parameter, Act, sets the signal processing method. Act can be null. The previously set signal processing method is saved to the third parameter oldact. If oldact is null, It is not saved.
0 is returned, and-1 is returned.

Struct sigaction {
Void (* sa_handler) (INT );
Void (* sa_sigaction) (INT, siginfo_t *, void *);
Sigset_t sa_mask;
Int sa_flags;
Void (* sa_restorer) (void);/* obsolete */
}
The second parameter type of the sa_handler and signal functions is the same. When the signal is delivered to the process, the sa_handler is called.
Sa_sigaction is also a function pointer for signal processing. It is called only when sa_flags contains sa_siginfo. Otherwise, the Linux kernel calls sa_handler by default. Siginfo_t contains the cause of signal generation. You do not need to assign values to sa_handler and sa_sigaction at the same time, because they may be a union.
The following code is the instance code of the function:

# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> void term_handler (int signum) <br/> {<br/> printf ("term handler % d/N", SIGNUM ); <br/>}< br/> int main (INT argc, char ** argv) <br/>{< br/> char Buf [100]; <br/> ssize_t ret; <br/> struct sigaction oterm; <br/> struct sigaction nterm; </P> <p> nterm. SA _ Handler = term_handler; <br/> nterm. sa_flags = 0; <br/> sigemptyset (& nterm. sa_mask); <br/> // If (signal (sigterm, term_handler) {<br/> If (-1 = sigaction (sigterm, & nterm, & oterm )) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}< br/> pause (); <br/> return 0; <br/>}

 

# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> struct sigaction oldact; <br/> struct sigaction Act; </P> <p> Ct. sa_handler = int_handler; <br/> act. sa_flags = 0; <br/> act. sa_flags | = sa_resethand; <br/> sigemptyset (& act. sa_mask); <br/> If (-1 = sigaction (SIGINT, & act, & oldact) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> // first signal <br/> pause (); <br/> printf ("return from first SIGINT signal! /N "); </P> <p> // second signal <br/> pause (); <br/> printf (" Never go here! /N "); </P> <p> return 0; <br/>}< br/>

The sa_mask signal shielding character. When the sa_handler signal processing function is executed, the signal specified by sa_mask will be blocked until the signal processing function returns. The signal corresponding to the current signal processing function is automatically added to sa_mask. Sa_flags is used to change the behavior during signal processing. When sa_flags contains sa_restart, the interrupted system call is automatically started after signal processing.

 

Default Value of the signal function

When a signal capture function is registered using the signal function, which default values are set by the signal function. You can use sigaction for observation.
# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> void print_mask (sigset_t sig_mask) <br/>{< br/> int I; </P> <p> for (I = 0; I <64; I ++) {<br/> If (sigismember (& sig_mask, I) {<br/> printf ("sig % d is masked! /N ", I); <br/>}</P> <p> void print_flags (INT flags) <br/>{< br/> If (flags & sa_nocldstop) {<br/> printf ("sa_nocldstop in flags! /N "); <br/>}< br/> If (flags & sa_restart) {<br/> printf (" sa_restart in flags! /N "); <br/>}< br/> If (flags & sa_onstack) {<br/> printf (" sa_onstack in flags! /N "); <br/>}< br/> If (flags & sa_nocldwait) {<br/> printf (" sa_nocldwait in flags! /N "); <br/>}< br/> If (flags & sa_nodefer) {<br/> printf (" sa_nodefer in flags! /N "); <br/>}< br/> If (flags & sa_resethand) {<br/> printf (" sa_resethand in flags! /N "); <br/>}< br/> If (flags & sa_siginfo) {<br/> printf (" sa_siginfo in flags! /N "); <br/>}</P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> char Buf [100]; <br/> ssize_t ret; <br/> struct sigaction oldact; </P> <p> If (signal (SIGINT, int_handler) = sig_err) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> bzero (& oldact, sizeof (oldact )); <br/> sigemptyset (& oldact. sa_mask); <br/> If (-1 = sigaction (SIGINT, null, & oldact) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> print_mask (oldact. sa_mask); <br/> print_flags (oldact. sa_flags); </P> <p> return 0; <br/>}< br/>

 

Signal and system call

Because the signal is asynchronous, it will happen anywhere in the program. As a result, the normal execution path of the program is broken and the signal processing function is executed.
Generally, when a process is executing a system call, the signal will not be delivered before the system call returns. Except for slow system calls, such as read/write terminals, networks, disks, and wait and pause. All these system calls will return-1, and errno is set to eintr. When the system call is interrupted, we can choose to use the loop to call again, or set to restart the system call (sa_restart ).

# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> char Buf [100]; <br/> ssize_t ret; <br/> struct sigaction olda CT; <br/> struct sigaction Act; </P> <p> act. sa_handler = int_handler; <br/> act. sa_flags = 0; <br/> // act. sa_flags | = sa_restart; <br/> sigemptyset (& act. sa_mask); <br/> If (-1 = sigaction (SIGINT, & act, & oldact) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> bzero (BUF, 100 ); <br/> ret = read (stdin_fileno, Buf, 10); <br/> If (ret =-1) {<br/> printf ("read error % s/n", strerror (errno); <br/> // return-1; <br/>}</P> <p> printf ("read % d bytes, content is % s/n", RET, Buf ); </P> <p> return 0; <br/>}< br/>

Signal sending function: Kill

The kill function can send specific signals to a process or process group.
# Include <sys/types. h> <br/> # include <signal. h> <br/> int kill (pid_t PID, int sig );
Parameters
PID> 0: send the signal to the process whose ID is PID;
PID = 0 send the signal to the Process Group ID equal to the Process Group ID of the sending process, and the sending process has the permission to send the signal to all processes;
PID <0 sends the signal to all processes whose process group ID is equal to the absolute value of the PID and the sending process has the permission to send the signal to it.
PID =-1 sends the signal to all processes except process 0, but the sending process must have permission.

0 is returned for success,-1 is returned for failure, and errno is set.
The basic rules with the permission to send signals are as follows: 1) a super user can send signals to any process; 2) the actual or valid user ID of the sender must be the actual or valid user ID of the receiver; when the value of SIGNUM is 0, kill still performs normal error detection, but does not send signals. This is often used to determine whether a specific process exists. If an empty signal is sent to an existing process, kill returns-1, and errno is set to esrch.

 

Signal sending function: Raise

The raise function sends a signal to the calling process, which is equivalent to kill (getpid (), SIGNUM)

0 is returned for success, and 0 is returned for failure.
# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> char choice; </P> <p> If (signal (SIGINT, int_handler) = sig_e Rr) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> printf (" Enter K for kill, or R for raise... /n "); </P> <p> while (true) {<br/> choice = getchar (); <br/> If ('R' = choice) {<br/> printf ("Raise SIGINT/N"); <br/> raise (SIGINT); <br/> break; <br/>}< br/> else if ('K' = choice) {<br/> printf ("Kill SIGINT/N "); <br/> kill (getpid (), SIGINT); <br/> break; <br/>}< br/> else {<br/> printf ("You enter % C is not correc T! /N ", choice); <br/>}</P> <p> return 0; <br/>}< br/>

Signal sending function: sigqueue

Sigqueue sends a signal sig to a specific process PID. You also need to check the permission before sending the signal.
The third parameter contains sa_siginfo in sa_flags only when the signal processing function is registered. The system will pass the value to the signal processing function in si_value in siginfo_t.
Union sigval {
Int sival_int;
Void * sival_ptr;
};

# Include <br/> <signal. h> <br/> int sigqueue (pid_t PID, int Sig, const Union sigval value );

0 is returned for success,-1 is returned for failure, and errno is set.

 

Signal shielding characters

Each process has a signal shielding word that specifies which signals can be delivered and which need to be blocked.
When a program executes sensitive tasks (such as updating a database), it does not want external signals to interrupt the program. In this phase, the signal is not simply ignored, but blocked. After the process completes the key tasks, the signal is also processed.

Signal shielding: sigprocmask

# Include <signal. h> <br/> int sigprocmask (INT how, const sigset_t * Set, sigset_t * oldset );

Sigprocmask can detect and modify the signal shielding characters of processes. When the oldset is not empty, the current signal shielding word is saved to the oldset. If set is not empty, the parameter "how" indicates how to modify the current blocked word.
How has three values:
The signal blocked by sig_block is the signal contained in the current shielded word plus the second parameter.
The signal contained in the second parameter of sig_unblock is removed from the current blocked word.
Sig_setmask is set to the value of the second parameter.# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> int flag = true; </P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> void quit_handler (int signum) <br/>{< br/> flag = false; <br/> printf ("in quit ha Ndler/N "); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> char choice; <br/> sigset_t mask; </P> <p> If (signal (SIGINT, int_handler) = sig_err) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> If (signal (sigquit, quit_handler) = sig_err) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> sigemptyset (& Mask ); <br/> If (-1 = sigaddset (& Mask, SIGINT) {<br/> printf ("add SIGINT to mask failed! /N "); <br/> return-1; <br/>}</P> <p> If (-1 = sigprocmask (sig_block, & mask, null) {<br/> printf ("sigprocmask failed! /N "); <br/> return-1; <br/>}</P> <p> printf (" SIGINT was masked, sigquit was registered! /N "); <br/> while (FLAG) {<br/> pause (); <br/>}</P> <p> If (-1 = sigprocmask (sig_unblock, & Mask, null) {<br/> printf ("sigprocmask failed! /N "); <br/> return-1; <br/>}</P> <p> printf (" SIGINT was unmasked, pauseing ......... /n "); <br/> pause (); </P> <p> return 0; <br/>}

 

Signal shielding: sigpending

 # Include <signal. h> <br/> int sigpending (sigset_t * Set );

 

Sigpending returns the current blocked delivery signal, which is generated but not sent to the process for processing. 0 is returned for success and-1 is returned for failure.
# Include <stdio. h> <br/> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <stdbool. h> <br/> # include <signal. h> <br/> # include <sys/types. h> <br/> # include <errno. h> <br/> # include <string. h> </P> <p> int flag = true; </P> <p> void int_handler (int signum) <br/>{< br/> printf ("int handler % d/N", SIGNUM ); <br/>}</P> <p> void quit_handler (int signum) <br/>{< br/> flag = false; <br/> printf ("in quit ha Ndler/N "); <br/>}</P> <p> int main (INT argc, char ** argv) <br/>{< br/> char choice; <br/> sigset_t mask; <br/> sigset_t pending; </P> <p> If (signal (SIGINT, int_handler) = sig_err) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> If (signal (sigquit, quit_handler) = sig_err) {<br/> printf ("sigaction failed! /N "); <br/> return-1; <br/>}</P> <p> sigemptyset (& pending ); <br/> sigemptyset (& Mask); <br/> If (-1 = sigaddset (& Mask, SIGINT )) {<br/> printf ("add SIGINT to mask failed! /N "); <br/> return-1; <br/>}</P> <p> If (-1 = sigprocmask (sig_block, & mask, null) {<br/> printf ("sigprocmask failed! /N "); <br/> return-1; <br/>}</P> <p> printf (" SIGINT was masked, sigquit was registered! /N "); <br/> while (FLAG) {<br/> pause (); <br/> If (-1 = sigpending (& pending )) {<br/> printf ("sigpending failed! /N "); <br/> return-1; <br/>}< br/> If (sigismember (& pending, SIGINT )) {<br/> printf ("SIGINT is in pending/n! "); <Br/>}</P> <p> If (-1 = sigprocmask (sig_unblock, & Mask, null )) {<br/> printf ("sigprocmask failed! /N "); <br/> return-1; <br/>}</P> <p>/* critical section */<br/> sleep (2 ); <br/> pause (); </P> <p> return 0; <br/>}

 

Signal shielding

In sigpending, if we call the sleep function to cancel a signal, the undelivered signal will be processed first. As a result, pause cannot be awakened.
The signal relief and waiting for signal delivery need to be made into an atomic operation, so that the above problems will not occur.

Signal shielding: sigsuspend

# Include <signal. h>
Int sigsuspend (const sigset_t * mask );

Sigsuspend sets the current signal shielding word to mask, and then suspends the calling process until a signal is delivered.
-1 is always returned because it is returned after the signal is interrupted!

 

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.