Atlas源码剖析(一)

信号设置

在设置各种配置参数之前,如果支持sigaction,则使用sigaction来设置SIGSEGV信号,指定处理函数调用g_on_error_stack_trace获取相关的堆栈信息后,调用abort退出程序。此信号在程序结束前进行清除,修复默认处理方式。

#ifdef HAVE_SIGACTION
        /* register the sigsegv interceptor */

        memset(&sigsegv_sa, 0, sizeof(sigsegv_sa));
        sigsegv_sa.sa_handler = sigsegv_handler;
        sigemptyset(&sigsegv_sa.sa_mask);

        if (frontend->invoke_dbg_on_crash && !(RUNNING_ON_VALGRIND)) {
                sigaction(SIGSEGV, &sigsegv_sa, NULL);
        }   
#endif

static void sigsegv_handler(int G_GNUC_UNUSED signum) {
        g_on_error_stack_trace(g_get_prgname());

        abort(); /* trigger a SIGABRT instead of just exiting */
}


同时,将SIGPIPE信号的处理方式设置为忽略。这里增加一些对SIGPIPE信号的介绍:TCP是全双工的信道,可以看作两条单工信道,,TCP连接两端的两个端点各负责一条。当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据。也就是说, 因为TCP协议的限制, 一个端点无法获知对端的socket是调用了close还是shutdown。

对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭。但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送)。但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据。所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出。

为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:

signal(SIGPIPE, SIG_IGN);

这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE。程序便能知道对端已经关闭。Atlas对于此信号的处理正是采用了这种方法。



在进入主流程后,通过libevent事件机制设置SIGTERM、SIGINT和SIGHUP信号,对于SIGTERM信号的处理方式是通过设置signal_shutdown标志,让进程从event_base_loop中退出(每个线程在进入event_base_loop之前,都会通过event_base_loopexit设置一个定时器,每隔1秒钟就退出event_base_loop,检查signal_shutdown是否被设置,如果设置了,就结束线程);SIGINT的处理跟SIGTERM使用同一个处理函数;SIGHUP的处理方式是设置日志中的rotate_logs标志,这导致在下一次写入日志时重新打开日志文件。

signal_set(&ev_sigterm, SIGTERM, sigterm_handler, NULL);
        event_base_set(chas->event_base, &ev_sigterm);
        signal_add(&ev_sigterm, NULL);

        signal_set(&ev_sigint, SIGINT, sigterm_handler, NULL);
        event_base_set(chas->event_base, &ev_sigint);
        signal_add(&ev_sigint, NULL);

#ifdef SIGHUP
        signal_set(&ev_sighup, SIGHUP, sighup_handler, chas);
        event_base_set(chas->event_base, &ev_sighup);
        if (signal_add(&ev_sighup, NULL)) {
                g_critical("%s: signal_add(SIGHUP) failed", G_STRLOC);
        }   
#endif

static void sigterm_handler(int G_GNUC_UNUSED fd, short G_GNUC_UNUSED event_type, void G_GNUC_UNUSED *_data) {
        chassis_set_shutdown_location(NULL);
}

void chassis_set_shutdown_location(const gchar* location) {
        if (signal_shutdown == 0) g_message("Initiating shutdown, requested from %s", (location != NULL ? location : "signal handler"));
        signal_shutdown = 1;
}

static void sighup_handler(int G_GNUC_UNUSED fd, short G_GNUC_UNUSED event_type, void *_data) {
        chassis *chas = _data;

        g_message("received a SIGHUP, closing log file"); /* this should go into the old logfile */

        chassis_log_set_logrotate(chas->log);

        g_message("re-opened log file after SIGHUP"); /* ... and this into the new one */
}

void chassis_log_set_logrotate(chassis_log *log) {
         log->rotate_logs = TRUE;
}


你可能感兴趣的:(Atlas)