默认的 SIGINT 处理程序是如何在它的库定义中实现的?
How is default SIGINT handler implemented in it's library definition?
Objective:由于某些奇怪的原因,将 This line must be printed
写入日志文件 mib_log_test
以防程序 hangs/stuck。
为简单起见,写了一个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 处理程序会导致在上述情况下不写入日志消息?
调用 default SIGINT handler terminates the process abnormally. This means _exit
,它不会刷新缓冲区。
作为旁注,调用 exit
(which does flush buffers) from a signal handler is unsafe (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 */
}
关键是实际的清理(通常不是 async-safe)不会发生在信号处理程序本身。
Stdio 文件缓冲区默认为 block-buffered,当它崩溃时它不会刷新文件缓冲区,因此缓冲输出会丢失。
一个解决方案是在每个 fprintf(fp, ...)
之后调用 fflush(fp)
,但这相当乏味。
另一个解决方案是在打开文件后立即使用 setvbuf
将文件设置为 line-buffered 模式,以便它在每个 new-line 符号上为您刷新缓冲区:
fp = fopen(FILE_NAME, "w+");
setvbuf(fp, NULL, _IOLBF, BUFSIZ);
这也使得 tail -f <logfile>
立即并逐行输出,而不是延迟并成块输出。
Objective:由于某些奇怪的原因,将 This line must be printed
写入日志文件 mib_log_test
以防程序 hangs/stuck。
为简单起见,写了一个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 处理程序会导致在上述情况下不写入日志消息?
调用 default SIGINT handler terminates the process abnormally. This means _exit
,它不会刷新缓冲区。
作为旁注,调用 exit
(which does flush buffers) from a signal handler is unsafe (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 */
}
关键是实际的清理(通常不是 async-safe)不会发生在信号处理程序本身。
Stdio 文件缓冲区默认为 block-buffered,当它崩溃时它不会刷新文件缓冲区,因此缓冲输出会丢失。
一个解决方案是在每个 fprintf(fp, ...)
之后调用 fflush(fp)
,但这相当乏味。
另一个解决方案是在打开文件后立即使用 setvbuf
将文件设置为 line-buffered 模式,以便它在每个 new-line 符号上为您刷新缓冲区:
fp = fopen(FILE_NAME, "w+");
setvbuf(fp, NULL, _IOLBF, BUFSIZ);
这也使得 tail -f <logfile>
立即并逐行输出,而不是延迟并成块输出。