C++ Signal(信号)

  • 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称软中断。从它的命名可以看出,它的实质和使用很像中断,所有,信号可以说是进程控制的一部分。
  • 信号signal处理是Linux程序的一个特色,用信号处理来模拟操作系统的中断功能。
  • #include <csignal>#include <signal.h> 是处理信号的C-library


1. 什么是信号

  • #include <csignal>#include <signal.h> 是处理信号的C-library。该库包含 signalraise 两个功能函数。
      I. 函数 signal 用于捕获信号,可指定信号处理的方式。
      II. 函数 raise产生一个信号,并向当前正在执行的程序发送该信号。

  • 信号 signal 可以理解为由操作系统传给程序(进程)的事件,只是用来通知程序发生了什么事件,并不会传递给该进程任何数据.
  • 信号是一种中断,因为它可以改变程序的流程。当信号传递给进程时,进程将停下其正在执行的操作,并去处理或忽略该信号 异步事件

   异步事件 - 查看信号的方式是一种处理异步事件的机制。
    程序通过 signal 函数捕获信号后,若 signal 函数第二参数为函数指针,则调用signal 函数的程序会阻塞(暂停在 signal 函数这句),直至异步线程(进入函数指针)return。程序将从暂停点恢复执行。
    此外,进程之间可以互相通过系统调用kill发送软中断信号

  • C++ signum.h 中通过 #define 定义了以下信号
    在这里插入图片描述

2. 函数 signal

  • 函数signal 用于捕获信号,可指定信号处理的方式,函数声明如下:
void (*signal(int sig, void (*func)(int)))(int);

第一个参数 sig 指明了所要处理的信号类型,它可以取除了SIGKILLSIGSTOP外的任何一种信号。
第二个参数描述了与信号关联的动作,下面将会对这三个参数进行讲解。

  • 当程序收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
        I. 默认处理:对信号进行该信号的系统默认处理,第二参数为 SIG_DFL
        II. 忽略信号:忽略该信号,第二参数为 SIG_IGN
        III. Function handler:指定处理函数,由该函数来处理,第二参数为 函数指针
  • signal函数中第二参数(函数指针)中通过 raise发出与signal函数对应信号类型相同的信号。程序将在signal函数这成为死循环,自己发信号,自己捕获,进程暂停。

2.1 默认处理-SIG_DFL

  • SIG_DFL 对信号进行该信号的系统默认处理。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>

using namespace std;
int main() {
    
    
    
    signal(SIGINT, SIG_DFL);
    while(1){
    
    
        std::this_thread::sleep_for (std::chrono::seconds(1));
        std::cout << "hello world!" << endl;
    }
    return 0;
}

hello world!
hello world!
hello world!
^C

  • 该程序包含一个死循环,循环内以一秒为间隔 打印语句。
  • SIGINT信号代表由InterruptKey产生,通常是CTRL +C 。执行上述代码时,按下CTRL + C 后程序退出。

2.2 忽略信号-SIG_IGN

  • SIG_IGN 表示忽略该信号。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>

using namespace std;
int main() {
    
    
    
    signal(SIGINT, SIG_IGN);
    while(1){
    
    
        std::this_thread::sleep_for (std::chrono::seconds(1));
        std::cout << "hello world!" << endl;
    }
    return 0;
}

hello world!
hello world!
^Chello world!
CChello world!
^Chello world!
hello world!
^\退出 (核心已转储)

  • 该程序包含一个死循环,循环内以一秒为间隔 打印语句。
  • SIGINT信号代表由InterruptKey产生,通常是CTRL +C 。执行上述代码时,按下CTRL + C 后程序没有反应。
  • 如果我们想结束该程序可以按下CTRL +\CTRL +\ 组合键会产生SIGQUIT信号,此信号并没有被忽略。

2.3 自定义函数

void (*signal(int sig, void (*func)(int)))(int);
  • 第二参数为 函数指针 :当signal函数捕获信号后,通过指定函数进行处理。
  • PS: 此函数必须在 signal() 被调用前申明。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>

using namespace std;

using namespace std;
void signalHandler( int signum ) {
    
    
    std::cout << "我来处理!" << std::endl;
    std::this_thread::sleep_for (std::chrono::seconds(5));
}

int main() {
    
    
    
    signal(SIGINT, signalHandler);			// 捕获 SIGINT
    std::this_thread::sleep_for (std::chrono::seconds(2));
        
    std::cout << "程序结束." << endl;
    
    return 0;
}

^C我来处理!
程序结束.

  • 该程序声明了信号类型为 SIGINTsignal函数,用于捕获 SIGINT信号。SIGINT信号代表由InterruptKey产生,通常是CTRL +C
  • 当什么都不做时,程序会正常运行到结束。
  • 如果在程序结束前按下CTRL +C ,函数捕获SIGINT信号,进入signalHandler函数,mian函数阻塞,直至signalHandler函数运行结束,mian函数打印后程序结束。

3. 函数 raise

  • 函数raise 产生一个信号sig,并向当前正在执行的程序发送信号sig,其声明如下:
int raise (int sig);
  • 重点: 值得注意的是,raise发出的信号,可被当前进程中同类型型号的signal函数捕获。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>

using namespace std;
void signalHandler( int signum ) {
    
    
    std::cout << "我来处理!" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    raise(SIGQUIT);                     // 3. 退出程序
}

int main() {
    
    
    
    signal(SIGINT, signalHandler);      // 1. signal函数,用于捕获 SIGINT 信号
    int i=0;

    while(++i){
    
    
        std::cout << "keep run...." << i << std::endl;
        if( i == 3 )
            raise(SIGINT);              // 2. raise, 发送SIGINT 信号
    }
    
    return 0;
}

keep run…1
keep run…2
keep run…3
我来处理!
退出 (核心已转储)

  • 可以看出当signal捕获信号后,main函数对应的程序暂停,不再打印。
  • 本测试中signalHandler()发出 SIGQUIT信号,进程结束。

4. 其他信号

  • linux系统下,运行命令 kill -l 可以看到Linux支持的信号列表。
    在这里插入图片描述

  • 一些常用的Signal 如下:

      Signal		Description
      SIGALRM		用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
      SIGBUS		某种特定的硬件异常,通常由内存访问引起
      SIGCANCEL		由Solaris Thread Library内部使用,通常不会使用
      SIGCHLD		进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
      SIGCONT		当被stop的进程恢复运行的时候,自动发送
      SIGEMT		和实现相关的硬件异常
      SIGFPE		数学相关的异常,如被0除,浮点溢出,等等
      SIGFREEZE		Solaris专用,Hiberate或者Suspended时候发送
      SIGHUP		发送给具有Terminal的Controlling Process,当terminal 被disconnect时候发送
      SIGILL		非法指令异常
      SIGINFO		BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
      SIGINT		由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程
      SIGIO			异步IO事件
      SIGIOT		实现相关的硬件异常,一般对应SIGABRT
      SIGKILL		无法处理和忽略。中止某个进程
      SIGLWP		由Solaris Thread Libray内部使用
      SIGPIPE		在reader中止之后写Pipe的时候发送
      SIGPOLL		当某个事件发送给Pollable Device的时候发送
      SIGPROF		Setitimer指定的Profiling Interval Timer所产生
      SIGPWR		和系统相关。和UPS相关。
      SIGQUIT		输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
      SIGSEGV		非法内存访问
      SIGSTKFLT		Linux专用,数学协处理器的栈异常
      SIGSTOP		中止进程。无法处理和忽略。
      SIGSYS		非法系统调用
      SIGTERM		请求中止进程,kill命令缺省发送
      SIGTHAW		Solaris专用,从Suspend恢复时候发送
      SIGTRAP		实现相关的硬件异常。一般是调试异常
      SIGTSTP		Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
      SIGTTIN		当Background Group的进程尝试读取Terminal的时候发送
      SIGTTOU		当Background Group的进程尝试写Terminal的时候发送
      SIGURG		当out-of-band data接收的时候可能发送
      SIGUSR1		用户自定义signal 1
      SIGUSR2		用户自定义signal 2
      SIGVTALRM		setitimer函数设置的Virtual Interval Timer超时的时候
      SIGWAITING	Solaris Thread Library内部实现专用
      SIGWINCH		当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
      SIGXCPU		当CPU时间限制超时的时候
      SIGXFSZ		进程超过文件大小限制
      SIGXRES		Solaris专用,进程超过资源限制的时候发
    

猜你喜欢

转载自blog.csdn.net/u013271656/article/details/114537411