1,在linux的信号机制里面,有很多信号的默认处理都是结束进程,例如SIGPIPE,SIGINT

如果我们没有对信号进行处理,那么我们的程序就不太健壮。

2,不同的操作系统,多线程中对信号的处理不一致。

linux的线程其实就是一个轻量级的进程,每一个线程都可以接收和处理信号。

例如,linux中信号处理默认是由主线程进行,但如果主线程对某个信号进行了屏蔽,这个信号就可以交给其它可以处理的线程进行处理。

3,为了统一,我们可以在主线程里面接收和处理信号,而其它线程屏蔽所有信号。

代码如下:

rbsignal.h

#ifndef _RB_SIGNAL_H
#define _RB_SIGNAL_H
#include <sys/signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>

void set_pipe(int p);

//阻塞所有的信号
void block_all_signal();


//主线程信号处理
void main_thread_sig_hand();

//信号是否在信号掩码中
int is_member(int sig);

#endif

rbsignal.cc

#include "rbsignal.h" 

static int rb_pipe;
void set_pipe(int p)
{
    rb_pipe = p;
}

//阻塞所有的信号
void block_all_signal()
{
    sigset_t mask;
    sigfillset(&mask);
    int rc = pthread_sigmask(SIG_BLOCK, &mask, NULL);
    if (rc != 0)
    {
        fprintf(stderr, "block all signal error: %s\n", strerror(rc));
        exit;
    }
}

//所有信号的处理函数
//就是向管道发一个信号值,以便在libevent循环中处理,目标:统一事件源
static void sig_handler(int sig)
{
    int save_errno = errno;
    int msg = sig;
    int r = write(rb_pipe, (char*)&msg, 4);
    errno = save_errno;
}

//安装一个信号处理程序
static void add_signal(int sig)
{
   struct sigaction action;
   memset(&action, 0, sizeof(action));
   action.sa_handler = sig_handler;
   sigfillset(&action.sa_mask);
   sigaction(sig, &action, NULL);
}

//主线程的信号处理
void main_thread_sig_hand()
{
        sigset_t except;
        sigemptyset(&except);
        sigaddset(&except, SIGHUP);
        sigaddset(&except, SIGPIPE);
        sigaddset(&except, SIGTERM);
        sigaddset(&except, SIGINT);
    int rc= pthread_sigmask(SIG_UNBLOCK, &except, NULL);
    if (rc != 0)
    {
        fprintf(stderr, "main thread signal error: %s\n", strerror(rc));
        exit;
    }      
    add_signal(SIGHUP);
    add_signal(SIGPIPE);
    add_signal(SIGTERM);
    add_signal(SIGINT);
}
//信号是否在信号掩码中
int is_member(int sig)
{
    sigset_t old;
    pthread_sigmask(SIG_SETMASK, NULL, &old);
    return sigismember(&old, sig);
}

以上两个文件是信号处理模块的相关接口,使用方法如下:

/*信号处理---在主线程生成多线程前阻塞所有信号,这样在子线程中可以继承主线程的信号处理,即阻塞所有信号*/
    block_all_signal();

/*初始化线程池*/
    thread_pool_init(thread_num);
/*为主线程添加信号处理---子线程生成完成后,再由主线程接收对信号的处理*/
  int pipefd[2];
pipe(pipefd); set_pipe(pipefd[1]); main_thread_sig_hand();
//在libevent统一的事件处理中,对信号进行处理
//......
event_set(&sig_event,pipefd[0], EV_READ|EV_PERSIST, sig_callback, NULL);
//......

void sig_callback(int fd,short ev,void *arg) { char c[4]; read(fd, c ,4); int sig = *(int*)c; switch(sig) { case SIGHUP: fprintf(stderr,"receive SIGHUP\n"); break; case SIGINT: fprintf(stderr,"receive SIGINT\n"); break; case SIGTERM: fprintf(stderr,"receive SIGTERM\n"); break; case SIGPIPE: fprintf(stderr,"receive SIGPIPE\n"); break; } }

代码可以下载:https://github.com/hxdoit/real_time_rank/tree/master/server1-1.0  (整个服务端)

https://github.com/hxdoit/real_time_rank/blob/master/server1-1.0/rbsignal.h  (信号相关的部分)

https://github.com/hxdoit/real_time_rank/blob/master/server1-1.0/rbsignal.cc (信号相关的部分)

https://github.com/hxdoit/real_time_rank/blob/master/server1-1.0/server.cc (信号相关的部分)