【问题标题】:How is default SIGINT handler implemented in it's library definition?默认 SIGINT 处理程序如何在其库定义中实现?
【发布时间】:2019-05-10 06:53:45
【问题描述】:

目标:将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 处理程序会导致不写入日志消息?

【问题讨论】:

    标签: c linux signals


    【解决方案1】:

    default SIGINT handler terminates the process abnormallyThis means _exit 被调用,它不会刷新缓冲区。

    附带说明,从信号处理程序调用exit确实刷新缓冲区)是不安全的 (only async-safe functions should be called from signal handlers)。所以,这不是你问题的真正解决方案。

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

    但是,冲洗可能相当昂贵。如果您想避免必须刷新每个日志行,但仍希望在接收 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 */
    }
    

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

    【讨论】:

    • 如我所说,上面只是一个示例代码,我的实际程序很长,有几十个fprintf,那么我应该在哪里使用fflush,多次使用是否有效?跨度>
    • @RaxeshOriya : fflush 的成本不可忽略,因此您可以根据需要经常使用它,但仅此而已。您必须根据自己的特定需求确定需要多久。但是,对于长时间运行的应用程序,处理信号的更好方法是在信号处理程序中设置一个标志,然后结束主循环,因此可以在退出之前进行适当的清理(例如stackoverflow.com/questions/4217037/catch-ctrl-c-in-c)。这里的关键是信号处理程序中不会发生清理。
    【解决方案2】:

    默认情况下,Stdio 文件缓冲区是块缓冲的,当它崩溃时,它不会刷新文件缓冲区,因此缓冲的输出会丢失。

    一种解决方案是在每个fprintf(fp, ...) 之后调用fflush(fp),但这相当乏味。

    另一种解决方案是在打开文件后立即使用setvbuf 将文件设置为行缓冲模式,以便在每个换行符上为您刷新缓冲区:

    fp = fopen(FILE_NAME, "w+");
    setvbuf(fp, NULL, _IOLBF, BUFSIZ);
    

    这也使得tail -f &lt;logfile&gt; 立即逐行输出,而不是延迟和块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-17
      • 2010-09-22
      • 1970-01-01
      • 2016-04-30
      • 2011-04-08
      • 2016-06-29
      • 1970-01-01
      • 2014-04-03
      相关资源
      最近更新 更多