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

