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





Обработчик 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 */
}
Суть в том, что фактическая очистка (которая часто не является асинхронной) не происходит в самом обработчике сигнала.
@RaxeshOriya : fflush имеет незначительную стоимость, поэтому вы используете его так часто, как это необходимо, но не более того. Как часто это необходимо, это то, что вам нужно будет определить для ваших конкретных потребностей. Однако для долго работающих приложений лучшим подходом к работе с сигналами является установка флага в обработчике сигналов, который затем завершает основной цикл, поэтому перед выходом можно выполнить надлежащую очистку (например, stackoverflow.com/questions/4217037/catch-ctrl-c-in-c). Ключевым моментом здесь является то, что очистка не происходит в обработчике сигнала.
Файловые буферы Stdio по умолчанию блочно-буферизованы, и при сбое он не очищает файловый буфер, так что буферизованный вывод теряется.
Одно из решений — вызывать fflush(fp) после каждого fprintf(fp, ...), но это довольно утомительно.
Другим решением является использование setvbuf для перевода файла в режим линейной буферизации сразу после открытия файла, чтобы он очищал буфер для вас при каждом символе новой строки:
fp = fopen(FILE_NAME, "w+");
setvbuf(fp, NULL, _IOLBF, BUFSIZ);
Это также делает вывод tail -f <logfile> немедленным и построчным, а не отложенным и блоками.
Как я уже сказал, это всего лишь пример кода, моя реальная программа довольно длинная и содержит десятки
fprintf, так где же мне использоватьfflushи эффективно ли использовать несколько раз?