#GCC programming # signal in Linux

Posted by seriousdamage on Wed, 02 Feb 2022 08:23:48 +0100

SIGNAL

Signals appeared in early Unix, and the early signal model is unreliable. BSD and System V extend the early signals respectively, but they are incompatible with each other. POSIX unifies the above two models, thus providing a reliable signal model.

1. Basic concept of signal

  • It is used for communication between processes. It can interrupt the running process of the process and change the processing flow
Process 1 sends a signal to process 2. Process 2 stops the current work and processes the signal instead to execute the corresponding work
  • The signal is a software interrupt
  • Signals are asynchronous events
Process 1 sends a signal to process 2. Process 1 does not need to wait for a response from process 2 and will continue to perform its own tasks.
- Unforeseen
- The signal has its own name and number, such as SIGINT
- Signal and exception handling mechanism
see linux Name and number of signals in the system, using kill -l Command.

The signal has no priority.
1~31 For non real-time signals, the transmitted signals may be lost and signal queuing is not supported. ( linux Used in the operating system, recorded in/usr/include/bits/signum.h In the file,Each signal has a certain explanation)
34~64 Real time signal, support signal queuing, and multiple real-time signals sent will be received.
  • Source of signal generation
Hardware source (driven by hardware)
1. Press the keyboard. CTRL+C CTRL+Z wait.
2. Some other hardware faults are caused by the hardware driver.

Software source (generated by kernel)
1. Common system functions for sending signals: kill(),raise(),alarm(),settimer()Wait.
2. Some illegal operations. Divide by 0, floating point arithmetic error, etc.
3. Software setting conditions (e.g gdb). 

2. Three signal processing methods

2.1. Ignore signal (signal, sig_ign)

SIGKILL and SIGSTOP It can never be ignored and cannot be captured. It's no use setting it.
Can be used to ignore hardware exception signals.
When the process starts, SIGUSR1 and SIGUSR2 Both signals are ignored by default. Register it and it can be captured and processed.

2.2. Execute the default operation signal(signo,SIG_DEF)

Each signal has its own default action, and the default action of most signals is to terminate the process.

2.3. Capture signal (signal, func)

Tell the kernel to call its own processing function when a signal occurs. signal Used to register signals and processing functions with the kernel.
SIGKILL and SIGSTOP Cannot be captured and ignored. Only default operations can be performed.

2.4 signal function

View the signal function and use the man signal command.

#include <signal.h>
void (*signal(   int signo,   void (*func)(int)  ))(int);

Function: register and install signal processing functions to the kernel.
Return: the pointer of the signal processing function is returned successfully, and the error is returned SIG_ERR
 Parameters: signo The signal value to be registered, func Signal processing function pointer or SIG_IGN Ignore signal or SIG_DEF Perform the default action.

2.5 reference examples

Capture and register SIGTSTP and SIGINT

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
//Define signal processing functions
//signo: signal captured by the process
void sig_handler(int signo)
{
    printf("%d,%d occured\n",getpid(),signo);
}

int main(void)
{
    //Register the signal processing function and signal value with the kernel 
    if(signal(SIGTSTP,sig_handler) == SIG_ERR){
        perror("signal sigtstp error");
    }
    if(signal(SIGINT,sig_handler) == SIG_ERR){
        perror("signal sigint error");
    }

    int i=0;
    while(i<30)
    {
        printf("%d out %d\n",getpid(),i++);
        sleep(1);//1 second
    }

}

After compiling and running, the keyboard combination CTRL+C CTRL+Z can no longer exit the interrupt.

kshine@kshine-virtual-machine:~/desktop $ gcc test.c -o bin
kshine@kshine-virtual-machine:~/desktop $ ./bin
6116 out 0
6116 out 1
6116 out 2
6116 out 3
6116 out 4
6116 out 5
6116 out 6
6116 out 7
6116 out 8
6116 out 9
6116 out 10
^C6116,2 occured    //CTRL+C terminate
6116 out 11
6116 out 12
6116 out 13
6116 out 14
^C6116,2 occured   //CTRL+C terminate
6116 out 15
6116 out 16
6116 out 17
6116 out 18
^Z6116,20 occured   //CTRL+Z pause
6116 out 19
6116 out 20
6116 out 21
6116 out 22
6116 out 23
6116 out 24
6116 out 25
^C6116,2 occured  //CTRL+C terminate
6116 out 26
6116 out 27
6116 out 28
6116 out 29

2.6 how can signals be used

have access to kill -SIGCONT  Process number to enable a process to resume operation.
You can also use kill -SIGSTP Process number to pause a process. and CTRL+Z Same function.
signal(SIGINT,SIG_IGN); //Can be used to ignore CTRL+C signals
signal(SIGINT,SIG_DFL); //The CTRL+C signal is used to terminate the process by default

2.7 SIGCHLD signal

In the learning process, I mentioned that in order to avoid zombie processes, there are two ways to close sub processes. First, the parent process uses wait() to block and wait for the child process to end and recycle. Second, the parent process ends, and process 1 takes the adopted child process and reclaims it.
Here, we can also use sigshield signal to reclaim sub processes in time. This allows the parent process to continue running without waiting for the child process to finish.

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
//Define signal processing functions
//signo: signal captured by the process
void sig_handler(int signo)
{
    printf("child processs dead, signo:%d\n",signo);
    //When the subprocess ends, SIGCHID signal will be generated. The parent process captures and wait s for the child process resources.
    wait(0);
}
void out(int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        printf("%d out %d\n",getpid(),i);
        sleep(2);
    }


}
int main(void)
{
    //Register the signal processing function and signal value with the kernel
    if(signal(SIGCHLD,sig_handler) == SIG_ERR){
        perror("signal sigchld error");
    }

    pid_t pid = fork();
    if(pid<0){
	perror("fork error");
        exit(1);
    }else if(pid>0){//Parent process
        out(100);
    }else if(pid==0){//Subprocess
        out(10);

    }

}

Operation results:

kshine@kshine-virtual-machine:~/desktop $ gcc test.c -o bin
kshine@kshine-virtual-machine:~/desktop $ ./bin 
9078 out 0
9079 out 0
9078 out 1
9079 out 1
9078 out 2
9079 out 2
9078 out 3
9079 out 3
9078 out 4
9079 out 4
9078 out 5
9079 out 5
9078 out 6
9079 out 6
9078 out 7
9079 out 7
9078 out 8
9079 out 8
9078 out 9
9079 out 9
9078 out 10
child processs dead, signo:17      
9078 out 11
9078 out 12
9078 out 13
9078 out 14
9078 out 15
^C
kshine@kshine-virtual-machine:~/desktop $ 

3. Signal transmission

3.1 basic concepts

  • Not all processes except the kernel and superuser root can send signals to other processes.
  • General processes can only send signals to processes with the same uid (user ID, created by the same user) and gid (user group ID, belonging to the same user group), or to other processes in the same process group.

3.1.1 function of transmitting signal

  • kill() sends signals to other processes or yourself. You can use man 2 kill to view the function introduction.
#include <sys/types.h>
#include <signal.h>
/*
sig It can be a null signal of 0 to detect whether a specific signal exists.
pid Process number.
(1)pid>0 Process ID
(2)pid ==0,Send signals to all processes in the same process group.
(3)pid < 0 Send a signal to all processes whose process group ID is equal to the absolute value of pid
(4)pid==-1 Send the signal to all processes on the system that the sending process has permission to send the signal.
*/
int kill(pid_t pid, int sig);
  • raise() can only send signals to itself
#include <signal.h>
int raise(int sig);

Reference examples:

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
//Define signal processing functions
//signo: signal captured by the process
void sig_handler(int signo)
{
    printf("signo:%d\n",signo);
}

int main(void)
{
    //Register the signal processing function and signal value with the kernel
    if(signal(SIGUSR1,sig_handler) == SIG_ERR){
        perror("signal sigusr1 error");
    }
    if(signal(SIGUSR2,sig_handler) == SIG_ERR){
        perror("signal sigusr2 error");
    }
    int i=0;
    while(i<10){
        printf("%d out %d\n",getpid(),i++);
        //if(i==5) kill(getpid(),SIGKILL); // Kill yourself
        sleep(1);
    }
    //Send a signal to the process itself
    raise(SIGUSR1);
    kill(getpid(),SIGUSR2);

}
kshine@kshine-virtual-machine:~/desktop $ gcc test.c -o bin
kshine@kshine-virtual-machine:~/desktop $ ./bin 
9567 out 0
9567 out 1
9567 out 2
9567 out 3
9567 out 4
9567 out 5
9567 out 6
9567 out 7
9567 out 8
9567 out 9
signo:10
signo:12
kshine@kshine-virtual-machine:~/desktop $ 
  • Alarm timing signal
    Set the timer. When the timer times out, SIGALRM signal will be generated.
    It is not a periodic timing signal, but a one-time signal.
    The accuracy is only seconds. To use higher accuracy, you can use the ualarm() function.
#include <unistd.h>
/*
seconds ==0 The previously set timer can be cancelled.
*/
unsigned int alarm(unsigned int seconds);

Reference examples:

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
//Define signal processing functions
//signo: signal captured by the process
void sig_handler(int signo)
{
    if(signo == SIGALRM){
	printf("clock time out\n");
        //Reset timer (periodic timing)
        alarm(5);
    }
    
}
void out(void)
{
    int i=0;
    while(i<20)
   {
      double d = drand48();
      printf("%-10d:%lf\n",i++,d);
      //if(i==16) alarm(0);// Cancel timer
      sleep(1);
   }

}

int main(void)
{
    //Register the signal processing function and signal value with the kernel
    if(signal(SIGALRM,sig_handler) == SIG_ERR){
        perror("signal sigalrm error");
    }
    
    //set timer 
    alarm(5);
    printf("begin running main\n");
    out();
    printf("end running main\n");
    return 0;
}

Operation results:

kshine@kshine-virtual-machine:~/desktop $ gcc test.c -o bin
kshine@kshine-virtual-machine:~/desktop $ ./bin 
begin running main
0         :0.000000
1         :0.000985
2         :0.041631
3         :0.176643
4         :0.364602
clock time out
5         :0.091331
6         :0.092298
7         :0.487217
8         :0.526750
9         :0.454433
clock time out
10        :0.233178
11        :0.831292
12        :0.931731
13        :0.568060
14        :0.556094
clock time out
15        :0.050832
16        :0.767051
17        :0.018915
18        :0.252360
19        :0.298197
clock time out
end running main
kshine@kshine-virtual-machine:~/desktop $ 
  • settimer()
  • abort() abnormal termination generates SIGABRT

4. High order lifting

4.1 signal reliability

  1. Unreliable signal problem I
    When a signal occurs, bind the signal again in the association action.
  • During the time between entering the associated action and calling the signal binding again in the associated action, the signal may be lost, that is, the same signal will not be captured
  • Cause the process to terminate
  1. Unreliable signal problem 2
    Unable to pause blocking signal.

4.2 function reentrancy

4.3 signal set

4.4 signal shielding

Topics: C Embedded system gcc