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

C语言写同一文件时的竞态条件问题:程序预期生成2MB文件实际仅1MB的原因咨询

多进程写文件竞态条件导致文件大小不符预期的问题分析

嘿,咱们一步步拆解你遇到的问题——预期生成2MB的文件结果只有1MB,核心问题出在代码的标志位设置错误和竞态条件处理失效上,再加上命令行参数的逻辑bug,直接导致了这个结果。

首先看最致命的两个问题:

1. O_APPEND标志位设置完全错误

你代码里的这行:

flags = O_CREAT | (O_APPEND & is_append) | O_WRONLY;

逻辑完全错了!O_APPEND是一个二进制标志位(Linux上通常是0x40),和is_append(1或0)做按位与的话,结果永远是0——因为O_APPEND的最低位是0。这意味着无论你是否设置is_append=1,程序都不会启用O_APPEND模式。

而O_APPEND的核心作用是:内核会把lseek到文件末尾 + write这两个操作变成原子操作,多进程写的时候不会互相覆盖。你现在等于完全没用到这个核心特性,自然会出现竞态条件。

正确的写法应该是用三元表达式判断:

flags = O_CREAT | O_WRONLY | (is_append ? O_APPEND : 0);

2. 非追加模式下的竞态条件无法解决

当你指定x参数(is_append=0)时,每次write前调用:

offset = lseek(fd, 0, SEEK_END);

然后再write,但lseek和write是两个独立的系统调用,不是原子的。比如两个进程同时执行:

  • 进程A:lseek到文件末尾(比如位置1000)
  • 进程B:也lseek到文件末尾(同样位置1000)
  • 进程A:write 1字节,文件位置变成1001
  • 进程B:write 1字节,会从位置1000开始写,直接覆盖进程A刚写入的内容

如果两个进程各尝试写入1MB,最终就会因为不断互相覆盖,总大小只有1MB,而不是预期的2MB。

还有命令行参数的逻辑bug

你的参数处理完全搞混了位置:

  • 假设你运行程序的命令是./prog f1 1048576(要写1MB)或者./prog f1 1048576 x(非追加模式),但代码里却把number_of_bytes赋值为atoi(argv[3])——这在3个参数的情况下argv[3]是NULL,atoi(NULL)返回0,会触发错误;在4个参数的情况下,argv[3]是x,atoi("x")也返回0,同样触发错误。

正确的应该是从argv[2]读取字节数:

number_of_bytes = atoi(argv[2]);

同时参数判断逻辑也要修正,比如:

// 正确的参数格式:程序名 文件名 字节数 [x]
if (argc < 3 || argc > 4) {
    usage_error();
    return -1;
}
if (argc == 4) {
    if (strcmp("x", argv[3]) != 0) {
        usage_error();
        return -1;
    }
    is_append = 0;
} else {
    is_append = 1;
}

修复后的效果

当你修正O_APPEND的设置后,使用默认的追加模式时,内核会保证每个write操作都是原子的——每个进程的write都会自动写到文件末尾,不会覆盖其他进程的内容。这时候两个进程各写1MB,最终文件大小就是2MB,符合预期。

如果一定要用非追加模式,那你需要引入文件锁(比如用fcntl加记录锁),在每次lseek+write前加锁,写完后解锁,避免多个进程同时操作文件位置。不过这种方式效率不如O_APPEND,推荐优先使用O_APPEND模式解决多进程写同一文件的竞态问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 06:22:40