Linux 异步信号处理机制

信号

概述

信号是软件中断。
信号是linux系统下的异步处理机制。
例如:在终端按写某些按键时,会通过信号机制停止一个程序。

首先,每个信号都有一个名字,都以SIG开头,通过 kill -l 可以查看
这里写图片描述
其中有很多常见信号
SIGINT :当我们在终端按下 CTRL+C 时就会产生一个 SIGINT 信号。
SIGQUIT :CTRL+\
SIGTSTP :CTRL+Z
SIGCHLD :子进程退出时会给父进程发送该信号。
还有很多例如 SIGABRT,SIGALRM也很常见,自行查阅。

信号基本概念

  • 发送信号
    产生信号,有多种发送信号的方式:

    • 一个进程向另一个进程发送一个特定信号。
    • 内核向用户进程发送一个信号。
    • 一个进程向自己发送一个信号。
  • 安装中断
    设置信号 到达时不再执行默认操作,而是执行自定义的代码。

  • 递送信号
    一个信号被操作系统发送到目标进程。

  • 捕获信号
    被递送的信号在目标进程引起某段处理程序的执行。

  • 屏蔽信号
    进程告诉操作系统暂时不接收某些信号,如果在屏蔽期间向进程发送被屏蔽的信号,则该信号不会被进程捕获。一段时间后,如果进程解除该信号的屏蔽,该信号将被捕获 。

  • 忽略信号
    进程被递送到目标进程,但目标进程不处理,直接丢弃。
  • 未决信号
    信号已经产生,但因目标进程暂时屏蔽该信号而不能被目标进程捕获的信号 。
  • 可靠信号与不可靠信号
    编号小于32的信号为不可靠信号 ,大于32的信号为可靠信号。
    如果进程在屏蔽某个信号的时间内,其他进程多次向其发送同一个信号,不可靠信号只有一次未决记录,当进程解除屏蔽时,该信号只会被捕获一次。而可靠信号操作系统或记录所有的发送,当进程解除屏蔽后,操作系统会捕获对等次数。

信号产生的方式

  1. 用户按某些终端键时将产生信号
    如在终端上按 “Ctrl + c”产生的终止信号。
  2. 硬件异常产生信号
    由硬件检测并通知内核,例:除0操作,CPU 的运算单元产生异常,内核将其解释为 SIGFPE 信号。例:对一个无效存储的访问,MMU会产生一个异常,内核将其解释为 SIGSEGV 信号。
  3. 终止进程信号
    其他进程调用 kill() 函数可将信号发送给另一个进程或进程组。
  4. 软件异常产生信号
    例:在管道的读进程已终止,后一个进程写此管道,会产生 SIGPIPE 信号。

信号处理方式

  1. 忽略此信号
    大多数信号可以使用这种方式进行处理,但是 SIGKILL 和 SIGSTOP 不能被忽略,它们用于向超级用户提供一种使进程终止或停止的可靠方法。
  2. 自定义捕捉信号
    当某信号到来时,执行用户自定义的操作,要求该进程先安装该信号,即通知内核在某种信号发生时调用一个特殊函数。
  3. 执行系统默认操作
    Linux系统对任何一个信号都规定了一个默认的操作。

信号在内核中的表示

这里写图片描述
信号在目的进程被注册,操作系统将信号添加到目的进程的PCB相关的数据结构中未决信号的数据成员 struct sigpending pending

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

信号操作

kill

我们利用 kill 指令杀死一个进程。

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

这里写图片描述
这里写图片描述

同样我们也可以在其他进程中调用kill函数杀死当前进程。

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

这里写图片描述
这里写图片描述
测试:
这里写图片描述

alarm

alarm() 函数用来传递定时信号 SIGALRM,该信号的默认处理动作是终止当前进程。

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

如果在调用 alarm 函数之前没有调用过 alarm 函数,则执行成功返回值为0,否则返回 -1。
如果在调用 alarm 函数之前调用过 alarm 函数,则将重新设置调用进程的闹钟。如果执行成功,将以当前时间为基准,返回值为上次设置的 alarm 将在多少时间内产生 SIGALRM 信号。失败返回 -1.
这里写图片描述
这里写图片描述

signal

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

这里写图片描述
这里写图片描述

由于signal正在逐步被淘汰,所以我们着重介绍 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 等自行查阅

这里写图片描述
这里写图片描述

信号操作集函数

设置进程屏蔽信号集

sigprocmask() 函数

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

获取当前未决信号

sigpending()

int sigpending(sigset_t *set);

成功返回0,将未决信号添加到 set 中,否则返回 -1

清空信号集

sigemptyset()函数

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

sigfillset() 函数

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

添加信号到信号集中

sigaddset()函数

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

从信号集中删除信号

sigdelset()函数

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

检测信号是否在信号集中

sigisemember()

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

这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/hudazhe/article/details/80005660