You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:35:22