ファイルを開いてからforkすると、そのファイルディスクリプタはシステムワイドなオープンファイルテーブルの同じ項目を指すので、書き込み位置(file offset)も共有され、同時に追加書き込みをしても競合は発生しないと思ったのですが、実験してみたら

log.txt
PARENT 24043 894 helloCPARENT 24043 895 hellCHPARENT 24043 896 helCHIPARENT 24043 897 heCHILPARENT 24043 898 hello

のようにおかしな部分が出ていました。環境はLinux 3.10.0です。

基礎的なことかと思いますが、どうしてこうなるのか教えていただけないでしょうか。

実験に使ったコード:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int fd;
    int i;
    char buf[256];

    fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);

    pid = fork();
    if (pid > 0) {
        for (i = 0; i < 1000; i++) {
            sprintf(buf, "PARENT %d %d hello\n", getpid(), i);
            write(fd, buf, strlen(buf));
        }
    }
    else if (pid == 0) {
        for (i = 0; i < 1000; i++) {
            sprintf(buf, "CHILD %d %d hello\n", getpid(), i);
            write(fd, buf, strlen(buf));
        }
    }
    else {
        perror("fork");
    }
    return 0;
}

画像の説明をここに入力

[追記]

逆に下記を ./a.out& ./a.out& で同時に動かしたとき、きれいにログが出ていました。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    int fd;
    int i;
    char buf[256];

    fd = open("log.txt", O_APPEND | O_WRONLY, 0644);

    for (i = 0; i < 1000; i++) {
        sprintf(buf, "This is %d %d hello\n", getpid(), i);
        write(fd, buf, strlen(buf));
    }

    return 0;
}