I understand that after calling fork() the child process inherits the per-process file descriptor table of its parent (pointing to the same system-wide open file tables). Hence, when opening a file in a parent process and then calling fork(), both the child and parent can write to that file without overwriting one another's output (due to a shared offset in the open-file table entry).
However, suppose that, we call open() on some file after a fork (in both the parent and the child). Will this create a separate entries in the system-wide open file table, with a separate set of offsets and read-write permission flags for the child (despite the fact that it's technically the same file)? I've tried looking this up and I don't seem to be able to find a clear answer.
I'm asking this mainly since I was playing around with writing to files, and it seems like only one the outputs of the parent and child ends up in the file in the aforementioned situation. This seemed to imply that there are separate entries in the open file table for the two separate open calls, and hence separate offsets, so the slower process overwrites the output of the other process.
To illustrate this, consider the following code:
int main(void) {
int fd;
if(!fork()) {
/* child */
fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);
write(fd, "hello ", 6);
_exit(0);
} else {
fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);
write(fd, "world\n", 6);
}
}
This will only print one of "hello" or "world. On the contrary, if we were to call open() prior to forking (and remove the two open calls afterwards), we would see "hello world" (or potentially "world hello"), which makes sense. So are there two different entries in the system open-file table?
According to POSIX, each call to open()
creates both a new open file descriptor and a new open file description. These are not shared with any other process. After a fork()
, the same file descriptor number in the parent and the child are separate file descriptors, but they refer to the same open file description. Similarly, a dup()
or dup2()
call creates a new file descriptor but it refers to the same open file description as the duplicated file descriptor refers to. (You can also use fcntl()
with F_DUPFD
or F_DUPFD_CLOEXEC
commands to duplicate a file descriptor; the new file descriptor also refers to the same open file description as the duplicated one.)
If the parent and child process after a fork()
both separately use open()
to open some file name, then (subject to timing issues related to file renaming etc) the two processes have separate file descriptors (of necessity — they're different processes), but they also have separate file descriptions and hence separate file offsets, even though they both access the same file. Any changes made by one can overwrite changes made by the other.
Note that the file offset (position) is a property of the open file description rather than of the open file descriptor; it can be shared between file descriptors, and even between processes.