Linux asynchronous signal processing mechanism

Signal

Overview

Signals are software interrupts.
Signal is an asynchronous processing mechanism under Linux system.
For example, when some keys are pressed and written in the terminal, a program will be stopped through a signal mechanism.

First of all, each signal has a name, which starts with SIG, which can be viewed by kill -l.
write picture description here
There are many common signals
SIGINT: when we press CTRL+C in the terminal, a SIGINT signal is generated.
SIGQUIT : CTRL+\
SIGTSTP : CTRL+Z
SIGCHLD : This signal is sent to the parent process when the child process exits.
There are many more, such as SIGABRT, SIGALRM is also very common, check it yourself.

Basic Concepts of Signals

  • Sending a signal : To
    generate a signal, there are many ways to send a signal:

    • A process sends a specific signal to another process.
    • The kernel sends a signal to the user process.
    • A process sends a signal to itself.
  • Install Interrupt
    Set the default action to execute when the signal arrives, but to execute custom code.

  • Delivering a signal
    A signal is sent by the operating system to the target process.

  • Catching a signal
    A signal that is delivered causes the execution of some handler in the target process.

  • Masking signals
    A process tells the operating system not to receive certain signals for a while, and if a masked signal is sent to the process during masking, the signal will not be caught by the process. After a period of time, if the process unmasks the signal, the signal will be caught.

  • Ignore the signal
    The process is delivered to the target process, but the target process does not handle it and discards it directly.
  • Pending signal
    A signal that has been generated but cannot be caught by the target process because the target process temporarily blocks the signal.
  • Reliable and unreliable signals Signals with
    numbers less than 32 are unreliable signals, and signals greater than 32 are reliable signals.
    If a process is blocking a signal and other processes send it the same signal multiple times, there is only one pending record for an unreliable signal, and when the process is unmasked, the signal will only be caught once. Whereas reliable signaling to the OS or logging all sends, the OS captures the peer count when the process is unblocked.

How the signal is generated

  1. Signals are generated when the user presses certain terminal keys
    such as the termination signal generated by pressing "Ctrl + c" on the terminal.
  2. The hardware exception generation signal is detected
    by the hardware and notified to the kernel, for example: division by 0 operation, the operation unit of the CPU generates an exception, and the kernel interprets it as a SIGFPE signal. Example: For an invalid memory access, the MMU will generate an exception, which the kernel interprets as a SIGSEGV signal.
  3. Killing Process Signals
    Another process calls the kill() function to send a signal to another process or process group.
  4. Software exception generates a signal
    Example : The read process of the pipe has been terminated, and the next process writes to the pipe, which will generate a SIGPIPE signal.

Signal processing method

  1. Ignore this signal
    Most signals can be handled this way, but SIGKILL and SIGSTOP cannot be ignored and are used to provide the superuser with a reliable way to kill or stop a process.
  2. User-defined capture signal
    When a signal arrives, perform a user-defined operation, requiring the process to install the signal first, that is, notify the kernel to call a special function when a certain signal occurs.
  3. Execute system default operation
    The Linux system specifies a default operation for any signal.

Representation of signals in the kernel

write picture description here
The signal is registered in the destination process, and the operating system adds the signal to the data member struct sigpending pending of the pending signal in the PCB-related data structure of the destination process

struct sigpending{
    struct sigqueue *head,**tail;
    sigset_t signal; // 未决信号集
};
struct sigqueue{  // 未决信号队列
    struct sigqueue *next;
    siginfo_t info;
};

Signal manipulation

kill

We use the kill command to kill a process.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
    while(1){
        printf("pid = %d\n",getpid());
        sleep(1);                                                                               
    }
    return 0;
}

write picture description here
write picture description here

Similarly, we can also call the kill function in other processes to kill the current process.

int kill(pid_t pid, int sig);
第一个参数为要传递信号的进程号,第二个参数为发送的信号值
pid > 0 : 将信号发送给进程的PID值为pid的进程
pid = 0 : 将信号发送给和当前进程在同一进程组的所有进程
pid = -1: 将信号发送给系统内调用者可以发送信号的所有进程
pid < 0 : 将信号发送给进程组号 PGID 为 pid 绝对值的所有进程
如果完成返回0,否则返回 -1,并设置 errno

write picture description here
write picture description here
test:
write picture description here

alarm

The alarm() function is used to pass the timing signal SIGALRM, the default processing action of this signal is to terminate the current process.

unsigned int alarm(unsigned int seconds);
在 second 秒内发送 SIGALRM 信号给当前进程
second = 0 取消之前所有先前发出的报警请求

If the alarm function has not been called before the alarm function is called, the return value is 0 if the execution is successful, otherwise it returns -1.
If the alarm function is called before the alarm function is called, the alarm for the calling process will be reset. If the execution is successful, it will be based on the current time, and the return value will be the time within which the last set alarm will generate the SIGALRM signal. Returns -1 on failure.
write picture description here
write picture description here

signal

sighandler_t signal(int sig, sighandler_t handler);
第一个参数为接收到的信号
第二个参数为接收到此信号后的处理代码入口
如果执行成功,此函数返回针对此信号的上一次设置

write picture description here
write picture description here

Since signal is being phased out, we focus on sigaction

sigaction

int sigaction(int sig, struct sigaction* act, struct sigaction *oact)
第一个参数为接收到的信号
第二,三个参数均为信号结构 sigaction 变量
第二个参数用来指定欲设置的信号处理方式
第三个参数将存储执行此函数前针对此函数的安装信息
struct sigaction{
    union{
        sighandler sa_handler;  // SIG_DFL SIG_INT 信号
        void (*sa_sigaction)(int sig, struct siginfo*, void *);
    }_u; //信号捕获函数
    sigset_t sa_mask; // 屏蔽信号集
    unsigned long sa_flags; // 标志
    void (*sa_restorer)(void); // 无使用
};
sa_flags:
    SA_NOCLDSTOP : 子进程退出时不生成 SIGCHLD 信号
    SA_ONSTACK : 与备用信号堆栈相关
    SA_RESETHAND,SA_RESTART,SA_SIGINFO 等自行查阅

write picture description here
write picture description here

Signal operation set function

Set process mask signal set

sigprocmask() function

int sigprocmask(int how, const sigset_t set, sigset_t oset);
第一个参数为更改该集的方式
SIG_BLOCK   将第二参数所描述的集合添加到当前进程屏蔽的信号集合中
SIG_UNBLOCK 将第二参数所描述的集合从当前进程屏蔽的信号集中删除
SIG_SETMASK 无视之前的屏蔽集合,设置当前进程屏蔽的集合为第二个参数描述的对象

Get the current pending signal

sigpending()

int sigpending(sigset_t *set);

Returns 0 on success to add pending signals to the set, otherwise returns -1

clear signal set

sigemptyset() function

int sigemptyset(sigset_t *set);
清空信号集

sigfillset() function

int sigfillset(sigset_t *set);
完全清空信号集

Add signal to signal set

sigaddset() function

int sigaddset(sigset_t *set, int signo);
第一个参数为添加信号的信号集,第二个参数为添加的信号

Remove a signal from the signal set

sigdelset() function

int sigdelset(sigset_t *set, int signo);
第一个参数为删除信号的信号集,第二个参数为删除的信号

Check if a signal is in a signal set

sigisemember ()

int sigissemptyset(const sigset_t *set, int _signo);

write picture description here
write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324603319&siteId=291194637