Linux signal programming

Posted by Clarkey_Boy on Tue, 08 Feb 2022 19:00:09 +0100

For Linux, the actual signal is a soft interrupt, similar to the hard interrupt in MCU. Many important programs need to process the signal. Signal, which provides a method for Linux to deal with asynchronous events. For example, if the end user inputs ctrl+c to interrupt a program, it will stop a program through the signal mechanism.

Signal overview
1. Name and number of signal:
Each signal has a name and number. These names start with "SIG", such as "SIGIO", "SIGCHLD", etc.
The signal is defined in signal In the H header file, the signal names are defined as positive integers.
For the specific signal name, you can use kill -l to check the signal name and serial number. The signal is numbered from 1, and there is no signal 0. Kill has special applications for signal 0.
2. Signal processing:
There are three methods of signal processing: ignore, capture and default action
1). Ignore signals. Most signals can be processed in this way, but there are two kinds of signals that cannot be ignored (SIGKILL and SIGSTOP respectively). Because they provide the kernel and super users with reliable methods to terminate and stop the process. If ignored, the process will become a process that no one can manage, which is obviously a scenario that the kernel designer does not want to see.
2). To capture a signal, you need to tell the kernel how the user wants to process a signal. To put it bluntly, you need to write a signal processing function, and then tell the kernel this function. When the signal is generated, the kernel calls the user-defined function to realize some signal processing.
3). System default action. For each signal, the system corresponds to the default processing action. When the signal occurs, the system will execute it automatically.
For specific signal default actions, you can use man 7 signal to view the specific definition of the system. Here, I won't expand it in detail. You can view what you need to view. You can also refer to P251-P256 in advanced programming in UNIX Environment (Part III) for a detailed description of each signal.

Linux signal ignore macro: SIG_IGN

After understanding the overview of signals, how are signals used?

In fact, the commonly used kill command is a tool for sending signals. kill 9 PID is used to kill the process. For example, I run a while1 process in the background. I can view its PID through ps -aux command, and send a signal to terminate the process through kill 9 to end the process. If you check the signal number and name, you can find that 9 corresponds to 9) SIGKILL, which is the signal that kills the process. The following execution process actually executes the default action of signal 9 - killing the process.

For the signal, the greatest significance is not to kill the signal, but to realize some means of asynchronous communication. So how to define the signal processing function?

Registration of signal processing functions
There is more than one way to register signal processing functions, which can be divided into entry-level version and advanced version
1. Entry version: function signal
2. Advanced version: function sigaction
Signal processing transmission function
There is more than one signal sending function, which is also divided into entry-level version and advanced version
1. Entry version: function kill
2. Advanced version: function sigqueue

==========================================================================
Signal registration function - entry
Function prototype of signal

#include <signal.h>
typedef void (*sighandler_t)(int);//Define a data type sighandler_t
sighandler_t signal(int signum, sighandler_t handler);

Parameter Description:
int: signal number
signum: the number of the registered signal
handler: the pointer of the interrupt function, that is, the name of the interrupt function

Signal sending function - entry Version (you can't carry messages while sending signals)
Function prototype of kill

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

Parameter Description:
pid: the pid of the receiver
sig: type of signal sent

Code case 1

//demo23.c
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
    printf("signum = %d\n",signum);
    switch(signum)
    {
       case 2:  printf("this is SIGINT\n");
                break;
       case 9:  printf("this is SIGKILL\n");
                break;
       case 10: printf("this is SIGUSR1\n");
                break;
    }
    printf("never quit\n");
}
int main()
{
    signal(SIGINT,handler);//Press Ctrl+c for the corresponding terminal
    signal(SIGKILL,handler);
    signal(SIGUSR1,handler);
    while(1);
    return 0;
}

Send signal through kill Command

Send signal by programming

//demo24.C
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
int main(int agrc,char** argv)
{
    char cmd[128]={0};
    int signum = atoi(argv[1]);
    int pid = atoi(argv[2]);
    //kill(pid,signum);  Or the following
    sprintf(cmd,"kill -%d %d",signum,pid);
    system(cmd);
    return 0;
}

Code case 2

//demo23.c
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
    printf("signum = %d\n",signum);
    switch(signum)
    {
       case 2:  printf("this is SIGINT\n");
                break;
       case 9:  printf("this is SIGKILL\n");
                break;
       case 10: printf("this is SIGUSR1\n");
                break;
    }
    printf("never quit\n");
}
int main()
{
    signal(SIGINT,SIG_IGN);//Press Ctrl+c for the corresponding terminal
    signal(SIGKILL,SIG_IGN);
    signal(SIGUSR1,handler);
    while(1);
    return 0;
}


It can be seen from the above cases that SIGKILL cannot be captured and ignored

==========================================================================
Signal registration function - Advanced Version (messages can be carried while sending signals)
Function prototype of sigaction

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
   void       (*sa_handler)(int); //The signal handler cannot accept additional data
   void       (*sa_sigaction)(int, siginfo_t *, void *); //The signal processing program can accept additional data and use it with sigqueue
   sigset_t   sa_mask;//Default blocking
   int        sa_flags;//Write SA_SIGINFO indicates that data can be accepted
 };

Parameter Description:
sigaction
signum: the number of the registered signal
act: it can be understood as handler + message in signal
oldact: back up the previous signal configuration to facilitate subsequent recovery. Write NULL and ignore

Focus on SA_ Parameters of sigaction()
int: same as signum, signal number
siginfo_t *: record some messages carried by the received signal

 siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
            pid_t    si_pid;      /* Process pid of sender*/
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
            sigval_t si_value;    /* Message sent by sender*/
            int      si_int;      /* Same as si_value.sival_int */
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}

void *: NULL means no message is received, and non NULL means a message is received

Signaling function - advanced version

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };

Parameter Description:
The same message passing value is more than kill

Code case 3

//demo25.c
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void handler(int signum,siginfo_t *info,void *context)
{
        printf("signum = %d\n",signum);
        if(context)
        {
                printf("get data = %d\n",info->si_int);
                printf("get data = %d\n",info->si_value.sival_int);
                printf("from sendpid = %d\n",info->si_pid);
        }
}
int main()
{
        struct sigaction act = { .sa_sigaction = handler , .sa_flags = SA_SIGINFO };
        printf("this is pid = %d\n",getpid());
        sigaction(SIGUSR1,&act,NULL);
        while(1);
        return 0;
}
//demo26.c
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int agrc,char** argv)
{
        int signum = atoi(argv[1]);
        int pid = atoi(argv[2]);
        union sigval value = {.sival_int = 100};
        sigqueue(pid,signum,value);
        printf("sendpid = %d\n",getpid());
        return 0;
}

Topics: Linux Embedded system