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

为何explain_fwrite提示文件被标记为非阻塞,fwrite返回EAGAIN?

问题根因分析

1. EAGAIN报错的原因

你没有主动设置O_NONBLOCK标记不代表文件描述符的标记没有被修改:

  • C标准库fopen返回的FILE*对应的底层文件描述符默认是阻塞模式,但如果你的代码其他位置对fileno(fd)返回的文件描述符执行过fcntl(F_SETFL)操作,误加了O_NONBLOCK标记,就会导致fwrite触发EAGAIN。如果你的项目引入了第三方库,也有可能是第三方库错误修改了已打开的文件描述符标记。
  • 少见场景下,如果你写入的/home目录挂载的是特殊文件系统(比如FUSE用户态文件系统、带特殊挂载参数的NFS/SMB网络文件系统),内核层面也可能直接返回EAGAIN,explain_fwrite只是按照通用场景给出解释。

2. ENOENT报错的原因

explain_fwrite给出的原因是推测性结论,并非100%匹配实际场景:

  • 你遇到的ENOENT不是因为文件被删除,是第一次fwrite触发EAGAIN失败后,FILE*的内部错误标记被置位,你没有清除错误标记就继续调用fwrite,会导致后续所有fwrite直接失败,errno保留旧值或者被错误设置,explain_fwrite读取到错误上下文就给出了不符合实际的原因推测。
  • 小概率场景是多线程运行时,你传入的file_to_write路径变量被其他线程修改,或者路径包含隐性不可见字符,但你确认文件始终存在的话,基本可以排除这类问题。

额外的代码逻辑错误

你的原始代码中fwrite的返回值判断逻辑本身是错误的:你调用fwrite时传入的参数是size=write_size, nmemb=1,这种情况下fwrite成功时返回的是成功写入的块数也就是1,不是write_size,只要write_size>1,你的判断条件n != write_size永远会成立,误判写入失败。

修复方案

  1. 校验并修正文件描述符的阻塞标记:在fopen之后、fwrite之前添加代码,清除可能存在的O_NONBLOCK标记:
// 打开文件后添加
int fd_num = fileno(fd);
int flags = fcntl(fd_num, F_GETFL, 0);
if (flags & O_NONBLOCK) {
    fcntl(fd_num, F_SETFL, flags & ~O_NONBLOCK);
}
  1. 修正fwrite的调用参数和判断逻辑,添加失败重试逻辑,保证数据完全写入:
// 将fwrite的参数调整为按字节计数,返回值就是实际写入的字节数
size_t total_written = 0;
size_t remain = write_size;
char* write_ptr = (char*)h->bufs[i];

while (remain > 0) {
    size_t n = fwrite(write_ptr, 1, remain, fd);
    if (n == 0) {
        int err = errno;
        // 信号中断、EAGAIN场景清除错误标记后重试
        if (err == EINTR || err == EAGAIN) {
            clearerr(fd);
            continue;
        }
        // 其他错误才判定为永久失败
        printf("fwrite permanent failed, errno=%d: %s\n", err, explain_fwrite(write_ptr, 1, remain, fd));
        break;
    }
    total_written += n;
    remain -= n;
    write_ptr += n;
}

内容的提问来源于stack exchange,提问作者Brydon Gibson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 06:15:03