In APUE section 8.3 fork function
, about file sharing between parent and child processes,
It said: It is important that the parent and the child share the same file offset.
And in section 8.9 Race Conditions
, there is a example: both parent and child write to
a file which is opened before invoking fork function. The program contains a race condition,
because the output depends on the order in which the processes are run by the kernel and for how long each process runs.
But in my test code, the output are overlapped.
[Langzi@Freedom apue]$ cat race.out
this is a long long outputhis is a long long output from parent
It seems the parent and child have separate file offsets instead of sharing the same offset.
Is there any error in my code? Or did I misunderstand the meaning of sharing offset?
Any advice and help will be appreciated.
following is my code:
#include "apue.h"
#include <fcntl.h>
void charatatime(int fd, char *);
int main()
{
pid_t pid;
int fd;
if ((fd = open("race.out", (O_WRONLY | O_CREAT | O_TRUNC),
S_IRUSR | S_IWUSR)) < 0)
err_sys("open error");
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0)
charatatime(fd, "this is a long long output from child\n");
else
charatatime(fd, "this is a long long output from parent\n");
exit(0);
}
void charatatime(int fd, char *str)
{
// try to make the two processes switch as often as possible
// to demonstrate the race condition.
// set synchronous flag for fd
set_fl(fd, O_SYNC);
while (*str) {
write(fd, str++, 1);
// make sure the data is write to disk
fdatasync(fd);
}
}
File descriptors are generally unique to each process, but they can be shared by child processes created with a fork subroutine or copied by the fcntl, dup, and dup2 subroutines.
When a fork() is performed, the child receives duplicates of all of the parent's file descriptors. These duplicates are made in the manner of dup(), which means that corresponding descriptors in the parent and the child refer to the same open file description.
When either the parent or child changes the contents of memory, whomever did the change gets a private copy of the changed memory (changes don't get shared); the other process keeps the "old" copy for himself. If all they do is read, no additional memory is used.
There is not really one executing before the other. It is simply that the parent will fork() then wait() for the child to complete. It may even fork several times if you use a piped series of commands for instance. Save this answer.
Parent and child share the same file table entry in the kernel, which includes the offset. It is impossible, then, for the parent and child to have different offsets without one or both of the processes closing and re-opening the file. So, any write by the parent uses this offset and modifies (increments) the offset. Then any write by the child uses the new offset, and modifies it. Writing a single character at a time aggravates this situation.
From my write(2) man page: "The adjustment of the file offset and the write operation are performed as an atomic step."
So, from that, you can be guaranteed that no write from one (parent or child) will write over top of the other's. You can also note that if you were to write(2) your whole sentence at once (in one call to write(2)), it is guaranteed that the sentence will be written together, in one piece.
In practice, many systems write log files this way. Many related processes (children of the same parent) will have a file descriptor that was opened by the parent. As long as each of them write a whole line at a time (with one call to write(2)), the log file will read as you would want it to. Writing a character at a time will not have the same guarantees. Use of output buffering (with, say, stdio) will similarly remove the guarantees.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With