Seeking a simple description regarding 'file descriptor' after fork()

It's important to distinguish between the file descriptor, which is a small integer that the process uses in its read and write calls to identify the file, and the file description, which is a structure in the kernel. The file offset is part of the file description. It lives in the kernel.

As an example, let's use this program:

#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(void)
{
    int fd;

    fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);

    if(!fork()) {
        /* child */
        write(fd, "hello ", 6);
        _exit(0);
    } else {
        /* parent */
        int status;

        wait(&status);
        write(fd, "world\n", 6);
    }
}

(All error checking has been omitted)

If we compile this program, call it hello, and run it like this:

./hello

here's what happens:

The program opens the output file, creating it if it didn't exist already or truncating it to zero size if it did exist. The kernel creates a file description (in the Linux kernel this is a struct file) and associates it with a file descriptor for the calling process (the lowest non-negative integer not already in use in that process's file descriptor table). The file descriptor is returned and assigned to fd in the program. For the sake of argument suppose that fd is 3.

The program does a fork(). The new child process gets a copy of its parent's file descriptor table, but the file description is not copied. Entry number 3 in both processes' file tables points to the same struct file.

The parent process waits while the child process writes. The child's write causes the first half of "hello world\n" to be stored in the file, and advances the file offset by 6. The file offset is in the struct file!

The child exits, the parent's wait() finishes, and the parent writes, using fd 3 which is still associated with the same file description that had its file offset updated by the child's write(). So the second half of the message is stored after the first part, not overwriting it as it would have done if the parent had a file offset of zero, which would be the case if the file description was not shared.

Finally the parent exits, and the kernel sees that the struct file is no longer in use and frees it.


In the same section, of book, there is a diagram showing three tables which are there when a file is opened.

The user filedescriptor table(part of the process table entry), filetable and inode table (v-node table). Now a filedescriptor (which is an index to the file descriptor table) entry points to a file table entry,which points to a inode table entry.
Now the file offset (the position from where the next read/write happens) is there in the File table.

So say you have a file opened in parent, that means it is having a descriptor, a file table entry and an inode reference too.
Now when you are creating a child the file descriptor table is copied for the child. So the reference count in the file table entry (for that open descriptor) is increased that means now there are two references for the same file table entry.

This descriptor is now available in both parent and child, pointing to the same File Table entry, hence sharing the offset. Now having this background let us see your questions,

  1. What does it mean? if parent's std output is redirected to a 'file1' for example, then what should child update after child writes? parent's original std output offset or redirected ouput(i.e file1) offset? Can't be the later, right?]

The child explicitly need not update anything. The author of the book is trying to
tell that, suppose parents's standard out put is redirected to a file and the a fork call is made. After that the parent is wating.So the descriptor is now duplicated, that is the file offset is also shared. Now whenever now the child writes anything to standard out, the written data is saved in the redirected file. The offset automatically is incremented by the write call.

Now say the child exits. So the parent come out of waiting and writes something on the standard out(which is redirected). Now where the parent 's write call's output will be placed -> after the data, which was written by the child. Why -> since the offset's current value is now changed after the child has written.

 Parent ( )
  {
    open a file for writing, that is get the 
    descriptor( say fd);
    close(1);//Closing stdout
    dup(fd); //Now writing to stdout  means writing to the file
    close(fd)
        //Create a child that is do a  fork call.
    ret = fork();
    if ( 0 == ret )
    {
        write(1, "Child", strlen("Child");
        exit ..
    }
        wait(); //Parent waits till child exit.

         write(1, "Parent", strlen("Parent");
    exit ..
}

Pl. see the above pseudo code, the final data that the opened file contains will be ChildParent. So you see that the file offset got changed when the child has written and this was avilable to the parent's write call, since the offest is shared.

2.How is the update done? by child explicitly, by OS implicitly, by files descriptor itself? After fork, i thought parent and child went their own ways and has their own COPY of file descriptor. So how does child update offset to parent side?]

Now I think the answer is clear-> by the system call that is by the OS.

[3. When fork() is invoked, all i understand is that child get a COPY of what parent has, file descriptor in this case, and does its thing. If any offset changes to file descriptor that parent and child share, it can only be because the descriptor remember the offset itself. Am i right?]

This should also be clear. The entry of the user file table points to the file table table entry (which contains the offset).

In other words, the system calls can fetch the offset from the descriptor.