Как реализован обработчик SIGINT по умолчанию в определении библиотеки? - PullRequest
2 голосов
/ 10 мая 2019

Цель: записать 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 по умолчанию, который приводит к тому, что в вышеописанном случае сообщения не записываются?

Ответы [ 2 ]

2 голосов
/ 10 мая 2019

Буферы файлов Stdio по умолчанию имеют блочную буферизацию и при сбое не очищают буфер файлов, так что буферизованный вывод теряется.

Одно из решений - звонить fflush(fp) после каждого fprintf(fp, ...), но это довольно утомительно.

Другое решение состоит в том, чтобы использовать setvbuf для перевода файла в режим буферизации строки сразу после открытия файла, чтобы он сбрасывал буфер для вас для каждого символа новой строки:

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

Это также заставляет tail -f <logfile> выводить сразу и построчно, а не с задержкой и в блоках.

2 голосов
/ 10 мая 2019

Обработчик 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 */
}

Ключ в том, что фактическая очистка (которая часто не является асинхронной) не происходит в самом обработчике сигнала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...