CentOS7中C程序遇errno9及多线程文件打开循环重试合理性咨询
在CentOS7中解决C程序errno 9(Bad file descriptor)问题
你当前的处理方式并不合理
直接循环调用fopen直到返回非NULL的做法有两个严重问题:
- 死循环风险:如果
fopen因为权限不足、磁盘满了、目标文件路径不存在等问题一直失败,这个循环会无限执行,占用大量CPU资源,直接拖垮程序。 - 忽略错误根源:你没有排查
fopen失败的具体原因,就算侥幸打开成功,潜在的问题(比如磁盘空间不足)也会继续存在,迟早再次引发异常。
另外,你遇到的errno 9本质是调用fclose(NULL)导致的——当fopen失败返回NULL时,直接执行fclose(log_fd)就是在关闭一个无效的文件描述符,这才是触发错误的核心原因。
更靠谱的处理方案
1. 基础错误检查(先避免崩溃)
最基本的修复是先判断fopen的返回值,只有成功打开文件时才执行写入和关闭操作,失败则记录错误并返回:
#include <errno.h> #include <string.h> static void __log_prt__(const char *fmt, va_list ap) { // 省略其他代码... FILE *log_fd = fopen(def_log_file, "a"); if (log_fd == NULL) { // 打印错误信息,方便后续排查问题 fprintf(stderr, "打开日志文件失败: %s, 错误原因: %s\n", def_log_file, strerror(errno)); return; } // 省略写入操作代码... // 关闭文件时也建议检查返回值,比如磁盘满了可能导致关闭失败 if (fclose(log_fd) != 0) { fprintf(stderr, "关闭日志文件失败: %s, 错误原因: %s\n", def_log_file, strerror(errno)); } }
2. 多线程场景优化(解决并发问题)
因为是多线程并发写入同一个日志文件,除了打开失败的问题,还会出现日志内容错乱的情况(多个线程同时写入时,日志行交织在一起),而且频繁打开/关闭文件也会影响性能。推荐两个优化方向:
- 全局预打开日志文件:程序启动时就打开日志文件,所有线程共享这个文件指针,避免每次写日志都调用
fopen/fclose。 - 加互斥锁保护写入:用
pthread_mutex_t确保同一时间只有一个线程写入日志,避免内容混乱。
示例代码:
#include <pthread.h> #include <errno.h> #include <string.h> static FILE *g_log_fd = NULL; static pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER; // 程序启动时调用,初始化日志文件 int log_init(const char *log_file) { g_log_fd = fopen(log_file, "a"); if (g_log_fd == NULL) { fprintf(stderr, "初始化日志文件失败: %s, 错误原因: %s\n", log_file, strerror(errno)); return -1; } // 设置行缓冲,确保日志写完就刷到磁盘,避免程序崩溃时丢失日志 setvbuf(g_log_fd, NULL, _IOLBF, 0); return 0; } // 程序退出前调用,关闭日志文件 void log_deinit() { if (g_log_fd != NULL) { fclose(g_log_fd); g_log_fd = NULL; } pthread_mutex_destroy(&g_log_mutex); } static void __log_prt__(const char *fmt, va_list ap) { if (g_log_fd == NULL) { fprintf(stderr, "日志文件未初始化\n"); return; } // 加锁,保证线程安全 pthread_mutex_lock(&g_log_mutex); // 执行写入操作 vfprintf(g_log_fd, fmt, ap); // 可按需添加换行或其他格式处理 pthread_mutex_unlock(&g_log_mutex); }
3. 可选的有限重试机制(别无限循环)
如果业务场景必须重试打开文件,不要用无限循环,设置重试次数上限和重试间隔,避免死循环:
#include <unistd.h> #include <errno.h> #include <string.h> static void __log_prt__(const char *fmt, va_list ap) { // 省略其他代码... FILE *log_fd = NULL; int retry_times = 3; // 最多重试3次 while (retry_times-- > 0) { log_fd = fopen(def_log_file, "a"); if (log_fd != NULL) { break; } // 每次重试前休眠100ms,避免频繁尝试占用CPU usleep(100000); } if (log_fd == NULL) { fprintf(stderr, "重试3次后仍无法打开日志文件: %s, 错误原因: %s\n", def_log_file, strerror(errno)); return; } // 省略写入操作代码... fclose(log_fd); }
内容的提问来源于stack exchange,提问作者czg
相关产品推荐
相关产品推荐

