I am new to the world of POSIX and I'm trying to understand how fork syscall works in C, especially how it copies file descriptors and buffers across parent to child. Specifically these two cases:
Case 1: I have a simple program that prints something
printf "start"
fork
parent:
print something
child
print something
This ends up printing "start" once and then the parent and child blocks. Which means fork was flushing i/o buffers before copying them across.
Case 2: Same program but now I am writing to a file instead of stdout. The file results in start printing twice, which is strange to me. Does fork only flush std buffers and not file buffers?
Program used for testing:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <fcntl.h>
int main() {
pid_t pid;
FILE *fd;
// Open a file in writing mode
fd = fopen("filename.txt", "w");
// Write some text to the file
fprintf(fd, "Started\n");
// Create a child process
pid = fork();
if (pid < 0) {
// Fork failed
fprintf(stderr, "Fork failed\n");
fclose(fd);
return 1;
} else if (pid == 0) {
// This is the child process
const char *child_message = "Hello from the child process!\n";
fprintf(fd, child_message);
fclose(fd); // Close the file descriptor in the child process
exit(0);
} else {
// This is the parent process
const char *parent_message = "Hello from the parent process!\n";
fprintf(fd, parent_message);
// Wait for the child process to complete
wait(NULL);
// Write a final message from the parent
const char *completion_message = "Child process completed.\n";
fprintf(fd, completion_message);
fclose(fd); // Close the file descriptor in the parent process
}
return 0;
}
First, fork()
doesn't flush buffers. (The term "buffer" across my answer refers to the C stdio buffer)
Second, keep these two points in mind
The output stream is always line buffered if and only if it is connected to a terminal device. Such as stdout
and stderr
, they both are this case. The output stream is fully buffered otherwise.
fork()
copy all the memory from parent to child. (We don't go into COW
here for less complexity)
Now, back to your question.
In your first example, only one "Start\n" is printed. It is because before fork()
, the buffer of stdout
has been flushed (line buffered). The buffer has nothing when it is copied to the child process.
In your second example, two "Start\n" is printed. It is because before fork()
, the buffer of the regular file has not been flushed (fully buffered). The buffer containing "Start\n" is copied to the child process.