22141


ファイルを開いてから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です。

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

実験に使ったコード：

[code]

    #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;
    }
    
[/code]

[![画像の説明をここに入力](https://i.stack.imgur.com/bC3fk.png)](https://i.stack.imgur.com/bC3fk.png)

[追記]

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

[code]

    #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;
    }
    
[/code]


CC BY-SA 3.0
2016-02-16T01:12:21.290
22132
2016-02-16T12:39:38.640
2016-02-16T01:34:34.477
13882
13882
question
linux c
forkしたプロセスから共有するファイルディスクリプタへの書き込みについて


[2016-02-16 21:40 誤読していたところがあったので修正しました。書き込み-
オフセット更新間の操作がアトミックに行われないのが問題なのではなく、オフセット更新がアトミックに行われない (オフセットが少しずつ増える) のが問題でした]

Linux カーネル 3.14 より前のバージョンに存在するバグのようです。  
手元の write(2) の man ページ に以下の記述がありました。共有するファイルデスクリプタに同時に `write()` を実行した場合、
~~書き込みと~~ オフセットの変更がアトミックに行われず、書き込み位置が被ることがあるとのことです。

> ### BUGS
>
> According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with
> Regular File Operations"):
>

>> All of the following functions shall be atomic with respect to each other
in the effects specified in POSIX.1-2008 when they operate on regular files or
symbolic links: ...

>
> Among the APIs subsequently listed are write() and writev(2). And among the
> effects that should be atomic across threads (and processes) are updates of
> the file offset. However, on Linux before version 3.14, this was not the
> case: if two processes that share an open file description (see open(2))
> perform a write() (or writev(2)) at the same time, then the I/O operations
> were not atomic with respect updating the file offset, with the result that
> the blocks of data output by the two processes might (incorrectly) overlap.
> This problem was fixed in Linux 3.14.


CC BY-SA 3.0
2016-02-16T02:34:57.623
22141
2016-02-16T12:39:38.640
2016-02-16T12:39:38.640
7831
7831
22132
answer