我有以下 C 代码,在 之后从父进程和子进程写入文件fork()
。但是, 中的输出testfile.txt
有时会损坏或出现意外顺序。
我附上了代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("testfile.txt", O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
perror("open");
return 1;
}
if (fork() == 0) {
// Child process
write(fd, "Child\n", 6);
close(fd);
} else {
// Parent process
write(fd, "Parent\n", 7);
close(fd);
}
return 0;
}
问题:
- 文件有时包含
"Child\nParent\n"
,有时包含"Parent\nChild\n"
- 在某些情况下,输出被损坏或混杂
- 我期望每个过程单独写入,但它们似乎互相干扰
问题:
- 为什么会发生这种情况?
- 我如何确保每个进程的写入正确且独立?
使用标志“O_APPEND”打开文件以避免损坏:
手册页显示:“文件以追加模式打开。每次写入(2)之前,文件偏移量都位于文件末尾,就像使用 lseek(2) 一样。文件偏移量的修改和写入操作作为单个原子步骤执行。”
输出顺序不受此标志控制。
您的父进程和子进程同时运行,且未采取任何措施来确保写入操作的同步。这意味着两次写入的顺序将是任意的,并且可能因系统首先调度哪个进程而有所不同。
此外,两个写入操作可能同时发生,内容可能会混合。对于像您这样的小写入,这在 POSIX 兼容系统上在技术上是不可能的,因为对于最多
PIPE_SIZE
1000 个字节的缓冲区,写入操作应该与其他并发写入器原子相关。尽管如此,对于较大的缓冲区,这是可能的。如果要避免这种情况,则必须使用某种形式的同步。常见的方法是:
如果您只想确保互斥性(即没有混合内容,一个写入将在另一个写入之前完全执行),那么通过
flock
系统调用进行简单的文件锁定就足够了。从技术上讲,这应该已经是没有锁定的情况了,因为正如我上面所说,这种小的写入在 Linux 上应该是原子的。以下是一个例子:
当两个进程中的一个持有锁时,另一个进程将阻塞调用
flock
,只有当另一个进程释放锁时调用才会完成。这确保了flock
和之间的关键部分close
得到执行。您将始终看到Child\nParent\n
或Parent\nChild\n
,永远不会看到混合内容。如果您还想保证两个操作的顺序,则必须使用更复杂的东西。管道或 eventfd(后者仅适用于 Linux)是实现此目的的一些最简单的工具。
下面是一个使用通过系统调用创建的管道的示例
pipe
:在这种情况下,您应该始终
Parent\nChild\n
在您的文件中看到。最后要注意的是:一定要检查的返回值
fork
,write
以及其他可能失败的系统调用或库函数调用。单次write
写入通常不能保证写入整个缓冲区,可能需要多次写入。