admin管理员组文章数量:1405144
I have the following C code that writes to a file from both the parent and child process after a fork()
. However, the output in testfile.txt
is sometimes corrupted or in an unexpected order.
I'm attaching the code:
#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;
}
Issue:
- The file sometimes contains
"Child\nParent\n"
and other times"Parent\nChild\n"
- In some cases, the output is corrupted or mixed
- I expected each process to write separately, but they seem to interfere with each other
Question:
- Why is this happening?
- How can I ensure correct, separate writes from each process?
I have the following C code that writes to a file from both the parent and child process after a fork()
. However, the output in testfile.txt
is sometimes corrupted or in an unexpected order.
I'm attaching the code:
#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;
}
Issue:
- The file sometimes contains
"Child\nParent\n"
and other times"Parent\nChild\n"
- In some cases, the output is corrupted or mixed
- I expected each process to write separately, but they seem to interfere with each other
Question:
- Why is this happening?
- How can I ensure correct, separate writes from each process?
- 2 That’s called a race condition. You have to do some sort of synchronization – Daniel A. White Commented Mar 8 at 19:47
- The code is not thread-safe and apparently creates race condition. The term race condition is not so much of a term; rather, it is a widely accepted jargon expression. More formal term would be something like incorrect dependency on the order of execution. Or unwanted dependency... – Sergey A Kryukov Commented Mar 8 at 20:00
- 1 @SergeyAKryukov 5.1.2.5 in the standard is called "Multi-threaded executions and data races" and in point 35: "The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior." - It doesn't describe what rules there are for processes competing for the same resource though since there is no concept of processes in the standard. So that's up to the OS. – Ted Lyngmo Commented Mar 8 at 20:21
- @Ted Lyngmo — What you quoted (why?) has nothing to do with “rules”. What are you trying to say? – Sergey A Kryukov Commented Mar 8 at 21:49
- 1 I quoted the formal definition of the term from the standard in response to your claim that it's merely jargon. – Ted Lyngmo Commented Mar 8 at 21:59
2 Answers
Reset to default 3Open the file with flag `O_APPEND` to avoid corruption:
The man page tells: "The file is opened in append mode. Before each write(2), the file offset is positioned at the end of the file, as if with lseek(2). The modification of the file offset and the write operation are performed as a single atomic step."
The output order is not controlled by this flag.
Your parent and children processes run concurrently without any measure in place to ensure synchronization of the write operation. This means that the order of the two writes will be arbitrary and can vary depending on which process is scheduled first by the system.
Moreover, the two write operations can occur at the same time and the content may get mixed. This is technically impossible on a POSIX compliant system for small writes like yours, because a write operation should be atomic wrt other concurrent writers for buffers up to PIPE_SIZE
bytes. Nonetheless, for larger buffers it is a possibility.
If you want to avoid this, you will have to use some form of synchronization. Common ways to do this are:
- Shared memory, where you can define mutexes or semaphores.
- IPC (inter-process communication) via various means including other file descriptors (pipes, sockets, eventfd), signals, etc.
- File locks.
If you only want to ensure mutual exclusivity (i.e. no mixed content, one write will fully execute before the other) then a simple file lock via the flock
syscall is enough. Technically this should already be the case without a lock because, as I said above, such small writes should be atomic on Linux.
Here's an example:
#include <err.h>
#include <sys/file.h>
/* ... */
if (fork() == 0) {
// Child process
// Acquire exclusive lock
if (flock(fd, LOCK_EX) != 0)
err(1, "child: flock failed");
write(fd, "Child\n", 6);
// Closing automatically releases the lock
close(fd);
} else {
// Parent process
// Acquire exclusive lock
if (flock(fd, LOCK_EX) != 0)
err(1, "parent: flock failed");
write(fd, "Parent\n", 7);
// Closing automatically releases the lock
close(fd);
}
When one of the two processes is holding the lock, the other process will block on the flock
call, which will only complete when the lock is released by the other process. This ensures that the critical sections between the flock
and the close
are executed. You will always see either Child\nParent\n
or Parent\nChild\n
, never some mixed content.
If instead you also want to guarantee the order of the two operations you will have to use something more complex. A pipe or an eventfd (the latter is Linux only) are some of the simplest tools you can use to achieve this.
Here's an example using a pipe created via the pipe
syscall:
#include <err.h>
#include <unistd.h>
/* ... */
// Create pipe for communication between parent and child
int pipefds[2];
if (pipe(pipefds) != 0)
err(1, "pipe failed");
if (fork() == 0) {
// Child process
// Wait for parent to write first
char tmp;
if (read(pipefds[0], &tmp, 1) != 1)
err(1, "child: read from pipe failed");
write(fd, "Child\n", 6);
close(fd);
} else {
// Parent process
write(fd, "Parent\n", 7);
close(fd);
// Signal child that it can now proceed
if (write(pipefds[1], "x", 1) != 1)
err(1, "parent: write to pipe failed");
}
In this case you should always see Parent\nChild\n
in your file.
As a final note: remember to always check the return value of fork
, write
and other syscalls or library function calls that can fail. A single write
generally does not guarantee that the entire buffer is written, more than one write may be required.
本文标签: cWhy does my forked process sometimes overwrite data in a fileStack Overflow
版权声明:本文标题:c - Why does my forked process sometimes overwrite data in a file? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744886872a2630540.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论