C++的Signal使用


C++ Signal使用

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

1.什么是信号

  • #include <csignal>#include <signal.h>是处理信号的C标准库,该库主要包含了signal()raise()两个功能函数。

    1. 函数signal用于捕获信号,可指定信号处理的方式。
    2. 函数raise产生一个信号,并向当前正在执行的程序发送该信号。
  • 信号signal可以理解为由操作系统传给程序(进程)的事件,只是用来通知程序发生了什么事件,并不会传递给该进程任何数据。

  • 信号是一种中断,因为它可以改变程序的流程。当信号传递给进程时,进程将停下其正在执行的操作,并去处理或忽略该信号异步事件。

异步事件 - 查看信号的方式是一种处理异步事件的机制。

  • 当程序通过signal函数捕获信号后,若signal函数第二参数为函数指针,则调用signal函数的程序会阻塞(暂停在signal函数这句),直至异步线程(进入函数指针)return。程序将从暂停点恢复执行。
  • 此外,进程之间可以互相通过系统调用kill发送软中断信号。
    (without define class style)
  • C++头文件signum.hsignum-generic.h中定义了以下信号
    /* We define here all the signal names listed in POSIX (1003.1-2008);
       as of 1003.1-2013, no additional signals have been added by POSIX.
       We also define here signal names that historically exist in every
       real-world POSIX variant (e.g. SIGWINCH).
    
       Signals in the 1-15 range are defined with their historical numbers.
       For other signals, we use the BSD numbers.
       There are two unallocated signal numbers in the 1-31 range: 7 and 29.
       Signal number 0 is reserved for use as kill(pid, 0), to test whether
       a process exists without sending it a signal.  */
    
    /* ISO C99 signals.  */
    #define	SIGINT		2	/* Interactive attention signal.  */
    #define	SIGILL		4	/* Illegal instruction.  */
    #define	SIGABRT		6	/* Abnormal termination.  */
    #define	SIGFPE		8	/* Erroneous arithmetic operation.  */
    #define	SIGSEGV		11	/* Invalid access to storage.  */
    #define	SIGTERM		15	/* Termination request.  */
    
    /* Historical signals specified by POSIX. */
    #define	SIGHUP		1	/* Hangup.  */
    #define	SIGQUIT		3	/* Quit.  */
    #define	SIGTRAP		5	/* Trace/breakpoint trap.  */
    #define	SIGKILL		9	/* Killed.  */
    #define SIGBUS		10	/* Bus error.  */
    #define	SIGSYS		12	/* Bad system call.  */
    #define	SIGPIPE		13	/* Broken pipe.  */
    #define	SIGALRM		14	/* Alarm clock.  */
    
    /* New(er) POSIX signals (1003.1-2008, 1003.1-2013).  */
    #define	SIGURG		16	/* Urgent data is available at a socket.  */
    #define	SIGSTOP		17	/* Stop, unblockable.  */
    #define	SIGTSTP		18	/* Keyboard stop.  */
    #define	SIGCONT		19	/* Continue.  */
    #define	SIGCHLD		20	/* Child terminated or stopped.  */
    #define	SIGTTIN		21	/* Background read from control terminal.  */
    #define	SIGTTOU		22	/* Background write to control terminal.  */
    #define	SIGPOLL		23	/* Pollable event occurred (System V).  */
    #define	SIGXCPU		24	/* CPU time limit exceeded.  */
    #define	SIGXFSZ		25	/* File size limit exceeded.  */
    #define	SIGVTALRM	26	/* Virtual timer expired.  */
    #define	SIGPROF		27	/* Profiling timer expired.  */
    #define	SIGUSR1		30	/* User-defined signal 1.  */
    #define	SIGUSR2		31	/* User-defined signal 2.  */
    
    /* Nonstandard signals found in all modern POSIX systems
       (including both BSD and Linux).  */
    #define	SIGWINCH	28	/* Window size change (4.3 BSD, Sun).  */
    
    /* Archaic names for compatibility.  */
    #define	SIGIO		SIGPOLL	/* I/O now possible (4.2 BSD).  */
    #define	SIGIOT		SIGABRT	/* IOT instruction, abort() on a PDP-11.  */
    #define	SIGCLD		SIGCHLD	/* Old System V name */

2.函数signal

函数signal 用于捕获信号,可指定信号处理的方式,函数声明如下:

void (*signal(int sig, void (*func)(int)))(int);

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

当程序收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
    1. 默认处理:对信号进行该信号的系统默认处理,第二参数为 SIG_DFL。
    2. 忽略信号:忽略该信号,第二参数为 SIG_IGN。
    3. 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;
}

该程序包含一个死循环,循环内以一秒为间隔 打印语句。
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;
}

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

2.3自定义函数

void (*signal(int sig, void (*func)(int)))(int);

第二参数为 函数指针 :当signal函数捕获信号后,通过指定函数进行处理。
此函数必须在 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;
}

该程序声明了信号类型为 SIGINT 的signal函数,用于捕获 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;
}

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


参考博客https://blog.csdn.net/u013271656/article/details/114537411


文章作者: Johnathon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Johnathon !
  目录