首页 > 解决方案 > 默认 SIGINT 处理程序如何在其库定义中实现?

问题描述

目标:写入This line must be printed日志文件mib_log_test以防程序由于某些奇怪的原因挂起/卡住。

为简单起见,写了一个 C 程序如下:

#include <stdio.h>
#include <stdlib.h>

#define FILE_NAME "./mib_log_test"
FILE *fp = NULL;

int main()
{
    fp = fopen(FILE_NAME, "w+");
    if (fp == NULL) {
        fprintf(stderr, "Unable to open %s file", FILE_NAME);
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "This line must be printed\n");
    while(1);
    return 0;
}

上面的程序编译运行后,永远不会因为无限循环而自行终止。所以我必须按下ctrl + c终止它。ctrl + c我没有看到写在This line must be printed我的日志文件中(mib_log_test

如果我如下所示覆盖默认的 SIGINT 处理程序,This line must be printed则会写入我的日志文件(mib_log_test)。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

#define FILE_NAME "./mib_log_test"
FILE *fp = NULL;

void sigint_handler(int sig_num)
{
    exit(EXIT_FAILURE);
}

int main()
{
    fp = fopen(FILE_NAME, "w+");
    if (fp == NULL) {
        fprintf(stderr, "Unable to open %s file", FILE_NAME);
        exit(EXIT_FAILURE);
    }
    signal(SIGINT, sigint_handler);
    fprintf(fp, "This line must be printed\n");
    while(1);
    return 0;
}

问题:什么默认的 SIGINT 处理程序会导致在上述情况下不写入日志消息?

标签: clinuxsignals

解决方案


默认的SIGINT 处理程序异常终止进程这意味着 _exit被调用,它不会刷新缓冲区。

作为旁注,从信号处理程序调用exit(它刷新缓冲区)是不安全的(只能从信号处理程序调用异步安全函数)。因此,这不是您问题的真正解决方案。

fflush(fp);如果您真的希望它出现在日志文件中,您可以在之后添加一个fprintf,即使进程异常终止也是如此。

然而,冲洗可能相当昂贵。如果您想避免必须刷新每个日志行,但仍希望在接收 SIGINT 时刷新日志文件,一种方法是:

#include <signal.h>

static volatile sig_atomic_t keepRunning = 1;

void sigHandler(int sig) {
  keepRunning = 0;
}

int main(void) {
  signal(SIGINT, sigHandler);

  while (keepRunning) {
    /* normal operation, including logging */
  }

  /* cleanup */

  return 0; /* this will close (and thus flush) the log file */
}

关键是实际的清理(通常不是异步安全的)不会发生在信号处理程序本身中。


推荐阅读