Signals in Linux

Signals in Linux

Concept and mechanism of signal

All signals received by each process are sent and processed by the kernel

Signal related events and states

Generate signal:
  1. Key generation: for example: Ctrl+C, Ctrl+z, Ctrl+\
  2. Generated by system call, such as kill, raise and abort
  3. Software condition generation, such as timer alarm
  4. Hardware exceptions occur, such as illegal access to memory (segment error), except 0 (floating point exception), memory alignment error (bus error)
  5. Command generation, such as kill Command
Delivery:

Delivery and arrival process

Pending:

The state between signal generation and delivery is mainly caused by blocking (or shielding)

Signal processing method:
  1. Execute the default action, and the signal processing is SIG_DFL(default);
  2. Ignore (discard) and set the signal processing to SIG_IGN (ignore), but there are two signals that cannot be ignored: SIGKILL and SIGSTOP;
  3. Capture (call the user's processing function signal handler to capture catching), but there are two signals that cannot be captured: SIGKILL and SIGSTOP.

The process control block PCB of Linux kernel is a structure, task_struct not only contains process id, status, working directory, user id, group id and file descriptor table, but also contains signal related information, mainly referring to blocking signal set and pending signal set.

Blocking signal set (signal mask word):

Some signals are added to the set and shielded. For example, if the signal is received after shielding the x signal, the signal will not be processed until the signal is unshielded.

Pending signal set:
  1. When the signal is generated, the description bit corresponding to the signal in the pending signal set immediately turns to 1, indicating that the signal is in the pending state; After the signal is processed, the description bit is immediately flipped to 0. Usually, the pending status is very short;
  2. After the signal is generated, it cannot be delivered due to some reasons (mainly blocking). The set composed of such signals is the pending signal set. The signal will remain pending until the mask is removed.
Sketch Map:

Signal number

You can use kill-l to view the signals available to the current system:

Signal 4 elements

  1. Number;
  2. name;
  3. event;
  4. Default processing action.

You can view the help document through man 7 signal. You can also view / usr/src/linux-headers-3.16.0-30/arch/s390/include/uapi/asm/signal.h

Some signals have three values. The first value is usually valid for alpha and sparc architectures, the middle value is for x86, arm and other architectures, and the last value is applied to mips architecture. A '-' indicates that the signal has not been defined on the corresponding architecture.

Default action:
  1. Term: terminate the process;
  2. Ign: ignore signal (ignore this signal in time by default);
  3. Core: terminate the process and generate the core file. (check the cause of death of the process for gdb debugging);
  4. Stop: stop (pause) the process;
  5. Cont: continue running the process.

Note: 9)SIGKILL and 19) SIGSTOP signals are not allowed to be ignored and captured.

List of common signals for Linux

1) SIGHUP: When the user exits shell When, by the shell All processes started will receive this signal, and the default action is to terminate the process 
2) SIGINT: When the user presses<Ctrl+C>When the key is combined, the user terminal sends this signal to the running program started by the terminal. The default action is to terminate the process. 
3) SIGQUIT: When the user presses<ctrl+\>The signal is generated when the key is combined, and the user terminal sends some signals to the running program started by the terminal. The default action is to terminate the process. 
4) SIGILL: CPU An illegal instruction was detected in a process. The default action is to terminate the process and generate core Documents;
5) SIGTRAP: The signal is generated by a breakpoint instruction or other trap Command generation. The default action is to terminate mileage and generate core File. 
6) SIGABRT: call abort Function. The default action is to terminate the process and generate core File. 
7) SIGBUS: Illegal access to memory address, including memory alignment error. The default action is to terminate the process and generate core File. 
8) SIGFPE: Issued when a fatal arithmetic error occurs. It includes not only floating-point operation errors, but also all algorithm errors such as overflow and divisor 0.
The default action is to terminate the process and generate core File. 
9) SIGKILL: Unconditionally terminate the process. This signal cannot be ignored, processed and blocked. The default action is to terminate the process. It provides system administrators with
 A method that can kill any process. 
10) SIGUSE1: User defined signal. That is, the programmer can define and use the signal in the program. The default action is to terminate the process. 
11) SIGSEGV: Indicates that the process has made an invalid memory access. The default action is to terminate the process and generate core File. 
12) SIGUSR2: Another user-defined signal that programmers can define and use in their programs. The default action is to terminate the process. 
13) SIGPIPE: Broken pipe Write data to a pipe without a read end. The default action is to terminate the process.
14) SIGALRM: The timer times out. The timeout time is called by the system alarm set up. The default action is to terminate the process. 
15) SIGTERM: Program end signal, and SIGKILL The difference is that the signal can be blocked and terminated. It is usually used to show that the program exits normally.
implement shell command Kill This signal is generated by default. The default action is to terminate the process. 
16) SIGSTKFLT: Linux Signals from earlier versions are still backward compatible. The default action is to terminate the process. 
17) SIGCHLD: When the state of the child process changes, the parent process will receive this signal. The default action is to ignore this signal. 
18) SIGCONT: If the process has stopped, let it continue. The default action is continue/Ignore. 
19) SIGSTOP: Stop the execution of the process. Signals cannot be ignored, processed and blocked. The default action is to pause the process. 

Signal generation

Terminal key generation
#  CTRL + C - > 2) SIGINT (terminate / interrupt) "int" - - > interrupt
#  CTRL + Z - > 20) sigtstp (pause / stop) "t" - - > Terminal
#  CTRL + C - > 3) sigquit  
Hardware exception generation
#  Divide by 0 - > 	 8)SIGFPE (floating point number exception) "F" - > float floating point number
#  Illegal access to memory - > 11) SIGSEGV (segment error) 
#  Bus error - > 	   7)SIGBUS  
kill function / command generation
#  Command format: Kill - sigkill PID (= = kill - 9 PID)
/*
Success: 0
 Failed: - 1, setting errno
 Possible causes of failure: illegal pid, illegal signal or insufficient user authority, etc
*/
int kill(pid_t pid, int sig);//pid is the process number and sig is the signal number. The macro name should be used to avoid inconsistent signal numbers of different operating systems
/*
pid>0: Send a signal to the process with process number pid;
pid=0: Send a signal to all processes that belong to the same process group as the process calling the kill function. 
pid<0: Send a signal to the process group where the absolute value of | pid | is located;
pid=-1: Send a signal to all processes in the system that the process has permission to send. 
*/
//Ordinary users cannot send signals, and ordinary users cannot use kill to send signals to root users.
For example, a child process is created in a loop, and the parent process terminates any child process with kill
Software condition generation
alarm function

Each process has only one timer

The alarm function will use the kernel to send 14) SIGALRM signals to the current process. After the process receives the signals, it will terminate by default

unsigned int alarm(unsigned int seconds);//Returns 0 or the number of seconds remaining. There is no failure

alarm(0) cancels the timer

Example: write a program to test how many can be counted in one second
#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
	alarm(1);

	int i=0;
	while(1){
		i++;
		printf("%d\n", i);
	}
	return 0;	
}
Use the time command to view the program execution time

time ./alarm

Actual execution time real = system time sys + user time user + waiting time

setitimer function

The timer can also be set, but the accuracy can be up to microseconds and can be executed cyclically.

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
/* which: Specify timing method:
			1.ITIMER_REAL:Natural time
            2.ITIMER_VIRTUAL(User space): only the CPU time occupied by the process is calculated
            3.ITIMER_PROF(User + kernel): calculate CPU consumption and system call execution time
    struct itimerval {
               struct timeval it_interval; // Interval for periodic timer 
               struct timeval it_value;    // Time until next expiration
           };
   struct timeval {
               time_t      tv_sec;         // seconds 
               suseconds_t tv_usec;        // microseconds 
           };
*/
Example: setimer implements alarm
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>  /* setitimer() */
#include <signal.h>	   /* signal() */

void sys_err(const char* str) {
	perror(str);
	exit(1);
}

void func() {
	printf("hello  world!\n");
}

int main(int argc, char* argv[]) {

	struct itimerval new_value;
	struct itimerval old_value;
	//Capture the alarm signal and perform the operation specified by func() function: the default behavior of SIGALRM is to terminate the process
	signal(SIGALRM, func);
    //Set alarm time 
	new_value.it_interval.tv_sec=1;
	new_value.it_interval.tv_usec=0;
    //Set cycle
	new_value.it_value.tv_sec=2;
	new_value.it_value.tv_usec=0;

	/*
	old_value.it_interval.tv_sec=1;
	old_value.it_interval.tv_usec=0;
	old_value.it_value.tv_sec=1;
	old_value.it_value.tv_usec=0;
	*/

	if(setitimer(ITIMER_REAL, &new_value, &old_value) == -1 ){
		sys_err("setitimer error");
	}

	while(1);//Block the program to see the phenomenon

	return 0;	
}

Signal set operation function

The kernel determines whether the signal is processed by reading the pending signal set.

The signal mask word mask can affect the pending signal set.

We can customize the set in the program to change the mask to shield the specified signal.

Signal set setting function

sigset_t set;	//typedef unsigned long sigset_t;
int sigemptyset(sigset* set); //Clear a signal set to zero 	 Success: 0 failure: - 1
int sigfillset(sigset* set); //Set a signal to 1 	 Success: 0 failure: - 1
int sigaddset(sigset_t *set, int signum);   //Adding a signal to the signal set succeeded: 0; Failed: - 1 
int sigdelset(sigset_t *set, int signum);   //Successfully clear a signal out of the signal set: 0; Failed: - 1
int sigismember(const sigset_t *set, int signum);//Judge whether a signal returns value in signal set: in set: 1; Not in: 0; Error: - 1   
//sigset_ The essence of type T is bitmap. However, bit operation should not be used directly, but the above functions should be used to ensure effective cross system operation. 
sigprocmask function
/*
This function is also used to mask and de mask signals. Its essence is to read or modify the signal shielding word of the process (in PCB) 
[Note] shielding signal: only postpone the signal processing (until the shielding is removed); And ignoring means that the signal is lost for processing
 Success: 0 failed: - 1 and set errno
*/
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
/*
	set:Pass in parameters;
	oldset:Outgoing parameters, save old signal mask set
	how:Operations on the current signal mask set:
		1)SIG_BLOCK: set Represents the signal set to be masked, which is equivalent to mask = mask|set
		2)SIG_UNBLOCK: set Indicates the signal set to be unshielded, which is equivalent to mask = Mask & ~ set
		3)SIG_SETMASK: set Represents a new set of masks to replace the original and. Equivalent to mask = set. If calling sigprocmask removes the blocking of several current signals, at least one signal will be delivered before sigprocmask returns. 
		how The first two methods are safer. Usually, a signal bit of set is set to 1, and then sig is used_ Block or SIG_UNBLOCK masks or unblocks the signal bit in the mask.
*/
sigpending function
Reads the pending semaphore set for the current process
int sigpending(sigset_t* set);//Set as outgoing parameter succeeded: 0 failed: - 1 set errno
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>

void sys_err(const char* str) {
	perror(str);
	exit(1);
}
void print_set(sigset_t* set){
	int i;
	for(i=1;i<32;i++){
		if(sigismember(set,i)){
			putchar('1');
		}else{
			putchar('0');
		}
	}
	printf("\n");

}

int main(int argc, char* argv[]) {
	sigset_t set, oldset, pedset;
	int ret=0;

	sigemptyset(&set);
	sigaddset(&set, SIGINT);

	ret=sigprocmask(SIG_BLOCK, &set, &oldset);
	if(ret==-1){
		sys_err("sigprocmask error");
	}

	while(1){
		ret=sigpending(&pedset);
		if(ret==-1){
			sys_err("sigpending error");
		}

		print_set(&pedset);
	}
	
	
	return 0;	
}

Signal capture

signal function

This function registers a signal capture function

typedef void (*sighandler_t)(int);//Function pointer sighandler_t: The parameter is int and the return value is void
sighandler_t signal(int signum, sighandler_t handler);
==void (*signal(int signum, void (*sighandler_t)(int))) (int);  

The above functions are old and should be avoided as far as possible

sigaction function
//Success: 0 failed: - 1 set errno
//act:in   oldact: out
int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };
//* 	 sa_handler: processing function after signal capture (i.e. registration function), SIG can also be used_ Ign (ignored) or SIG_DFL (default action);
//	sa_sigaction: when sa_flags is specified as SA_ When siginfo, use the signal processing program (but rarely);
//* 	 sa_mask: the signal set (signal mask word) to be shielded when calling the signal processing function. Note: masking takes effect only when the processing function is called. It is a temporary setting. 
//* 	 sa_flags: usually set to 0, and the table uses the default attribute. 
//	sa_restorer: (the element is deprecated)
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>

void sys_err(const char* str) {
	perror(str);
	exit(1);
}

void sig_catch(int signo) {
	printf("catch you, %d \n", signo);
}

int main(int argc, char* argv[]) {
	struct sigaction act,oldact;

	act.sa_handler = sig_catch;	//Set callback function
	sigemptyset(&(act.sa_mask));//Clear signal mask word
	act.sa_flags = 0;			//Set flag bit

	int ret = sigaction(SIGINT, &act, &oldact);
	if( ret == -1) {
		sys_err("sigaction error");
	}

	while(1);

	return 0;	
}

Signal capture characteristics

1. During the execution of the capture function, the signal mask word is mask --> sa_mak, After the execution of the capture function, it is restored to mask;
2. This signal is automatically masked during the execution of the capture function(sa_flag=0);
3. During the execution of the capture function, the masked signal is sent many times and processed only once after the masking is removed.
The kernel implements the signal capture process:

SIGCHLD signal

Production conditions

  1. When the subprocess terminates;
  2. When the subprocess stops after receiving the SIGSTOP signal;
  3. The child process is in the stop state and wakes up after receiving SIGCONT.

Recovering subprocesses with SIGCHLD signal

code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>

void sys_err(char* str) {
	perror(str);
	exit(1);
}

void catch_child(int signo) {
	int status;
	pid_t wpid;

	/*
	wpid = wait(NULL);
	printf("catch you, %d\n", wpid);
	*/

	while((wpid = waitpid(0, &status, WNOHANG)) > 0) {	//Recycle the subprocess. Because the SIGCHLD signal does not support queuing, it needs to recycle to avoid zombie processes
		if(WIFEXITED(status)) {
			printf("child %d exit %d \n",wpid, WEXITSTATUS(status));
		} else if(WIFSIGNALED(status)) {
			printf("child %d cancel signal %d \n", wpid, WTERMSIG(status));
		}
	}

	return;
}

int main() {
	pid_t pid;
	int i;
//Blocking signal
	for(i =0; i<5; i++) {
		if((pid=fork())==0) {	//Create multiple child processes
			break;
		} else if(pid < 0) {
			sys_err("fork error");
		}
	}

	if(pid == 0) {
		int n = 1;
		while(n--) {
			printf("child id %d\n", getpid());
			sleep(1);
		}
		return i*i;
	} else if(pid > 0) {
		//sleep(1);
		struct sigaction act,oldact;	//Register signal capture function
		act.sa_handler=catch_child;
		sigemptyset(&(act.sa_mask));
		act.sa_flags=0;
		sigaction(SIGCHLD,&act,&oldact);
//Unblock
		while(1) {
			printf("I'm parent: %d\n",getpid());
			sleep(1);
		}
		
	} 
	return 0;
}

SIGCHLD signal precautions

  1. The child process inherits the signal mask word and signal processing action of the parent process, but does not inherit the pending signal set;
  2. It should be noted that the location of the registered signal capture function should be in the parent process;
  3. The sigcld signal should be blocked before fork, and unblocked after registering the capture function to avoid that the parent process has no time to capture the sigcld signal of the child process.

Interrupt system call

  1. Slow system call: calls that may cause the process to permanently call blocking, such as read,write,wait
  2. Other system calls: such as getpid,fork

When a slow system call is interrupted, the SA can be modified_ Flags parameter to set whether the system call is restarted after being interrupted by the signal. Set to SA_INTERRUPT means no restart, SA_RESET means restart.

Extension: sa_flags has other parameters. After a signal is captured, you do not want to automatically block the signal during the execution of the capture function. You can set SA_ Set flags to sa_ Noder, unless SA_ The signal is included in the mask (blocking it).

,&act,&oldact);
//Unblock
while(1) {
printf("I'm parent: %d\n",getpid());
sleep(1);
}

} 
return 0;

}

#### SIGCHLD signal precautions

1. The child process inherits the signal mask word and signal processing action of the parent process, but does not inherit the pending signal set;
2. It should be noted that the location of the registered signal capture function should be in the parent process;
3. Should be fork Previous blocking SIGCHLD After registering the capture function, the blocking is removed to avoid that the parent process has no time to capture the data of the child process SIGCHLD Signal.

### Interrupt system call

1. Slow system call: a call that may permanently block the process, such as: read,write,wait......
2. Other system calls, such as: getpid,fork......

When the slow system call is interrupted, the modification can be used sa_flags Parameter to set whether the system call is restarted after being interrupted by the signal. Set to SA_INTERRUPT Indicates no restart, SA_RESET Indicates restart.

Extension: sa_flags There are other parameters. After capturing the signal, you do not want to automatically block the signal during the execution of the capture function sa_flags Set to SA_NODEER,Unless sa_mask The signal is contained in the (blocking it).

Tags: Linux

Posted on Wed, 08 Sep 2021 17:10:41 -0400 by bzenker