Bootstrap

C/C++ 信号处理 Signal

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

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

函数 signal

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

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

第一个参数 sig 指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。

第二个参数描述了与信号关联的动作,下面将会对这三个参数进行讲解。

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

函数 raise

  • 函数raise 产生一个信号sig,并向当前正在执行的程序发送信号sig,其声明如下:

    int raise (int sig);

  • 重点: 值得注意的是,raise发出的信号,可被当前进程中同类型型号的signal函数捕获。

处理函数:

static void sig_handler(int signo)
{
  char Siglog[256] = { 0 };
  if (SIGPIPE == signo)
  {
    fprintf(stderr, "\n\n\nwarning: ###########################################################################################SIGPIPE(%d) catched\n\n\n", signo);
    return;
  }
  fprintf(stderr, "######################################################################################################Signal %d, exiting...\n", signo);
  
  memset(Siglog,0,256);
  sprintf(Siglog,"########################Signal %d, exiting...########################\n", signo);
  syslog(LOG_DEBUG, "%s\n", Siglog);
  lxLogwarn(3, Siglog);

  mainDestory();
  exit(EXIT_FAILURE);
}


static void signal_init(void)
{
  /*忽略sigpipe(tcp 当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送SIGPIPE信号。)
  为了防止客户端进程终止,而导致服务器进程被SIGPIPE信号终止,因此服务器程序要处理SIGPIPE信号*/
  signal(SIGPIPE, SIG_IGN);
  //总线错误
  if (signal(SIGBUS, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGBUS!");
  //数学相关的异常,如被0除,浮点溢出,等等
  if (signal(SIGFPE, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGFPE!");
  //session回话结束
  if (signal(SIGHUP, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGHUP!");
  //非法指令
  if (signal(SIGILL, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGILL!");
  //Ctrl+C
  if (signal(SIGINT, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGINT!");
  //if (signal(SIGPIPE, sig_handler) == SIG_ERR)
  //  fprintf(stderr, "Can't catch SIGPIPE!");
  //类似sigint
  if (signal(SIGQUIT, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGQUIT!");
  //段错误 非法内存访问
  if (signal(SIGSEGV, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGSEGV!");
  //系统调用中参数错误
  //if (signal(SIGSYS, sig_handler) == SIG_ERR)
  //  fprintf(stderr, "Can't catch SIGSYS!");
  //程序结束
  if (signal(SIGTERM, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGTERM!");
  if (signal(SIGTRAP, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGTRAP!");
  //由调用abort函数产生,进程非正常退出
  if (signal(SIGABRT, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGTRAP!");
  if (signal(SIGUSR1, sig_handler) == SIG_ERR)
    fprintf(stderr, "Can't catch SIGTRAP!");
}

常用信号量:

  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专用,进程超过资源限制的时候发

函数 atexit

atexit()用来注册终止处理程序,当程序通过调用exit()或从main 中返回时被调用.终止处理程序执行的顺序和注册时的顺序是相反的,终止处理程序没有参数传递.同一个函数若注册多次,那它也会被调用多次.按POSIX.1-2001规定,至少可以注册32个终止处理程序,若想查看实际可以注册多少个终止处理程序,可以通过调用sysconf()函数获得.

当一个子进程是通过调用fork()函数产生时,它将继承父进程的所有终止处理程序.在成功调用exec系列函数后,所有的终止处理程序都会被删除.

;