Cでmmapを使ったプロセス間通信


下記のコードはmmapを使ったプロセス間通信の例なのですが、なぜ、下記のようにするとプロセス間でデータがやり取りできるのでしょうか?

`for`以下の所で`*s`(sの先頭)が`1`か`0`になるまで待ち、その後に子プロセスでは`*s`に`0`を代入し、親プロセスでは`*(s+1) =
'a'`としています

なぜ、このような処理でプロセス間でデータのやり取りをしたことになるのでしょうか?

参考:[知ってトクするシステムコール（5）：システムコールを減らすシステムコール (2/2) -
＠IT](http://www.atmarkit.co.jp/ait/articles/1205/28/news125_2.html)

[code]

    
    #include <unistd.h>
    #include <sys/mman.h>
    #include <stdio.h>
    
    int main() {
        int i, rp=1000000;
        char *s;
        s = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
        *s = '0';
        if (0 == fork()) {
            for (i = 0; i < rp; i++) {
                while ('0' == *s)
                    ;
                *s = '0';
            }
        }
        else {
            for (i = 0; i < rp; i++) {
                while ('1' == *s)
                    ;
                *(s+1) = 'a';
                *s = '1';
            }
        }
    }
    
    
[/code]




プロセス間通信という意味では、 `*(s+1) = 'a';` や `*s = '0';` や `*s = '1';`
をした時点で、それは即座に相手にも見えるようになります (キャッシュ構成の影響などで遅れることもありますが)。

コードの意図としては、親プロセスと子プロセスの間で `*(s+1)` を使って `'a'` を送信した・受信したという確認のやりとりのつもりで、 `*s`
に `'0'` や `'1'` を読み書きしてるのではと想像します。

このコードの別の問題点は、最適化つきでコンパイルするとまともに動かないことでしょう。次のようにコンパイルしたら fork()
の後に無限ループする結果になってしまいました。

[code]

    $ gcc -S -O3 a.c -o -
        .file   "a.c"
        .section    .text.unlikely,"ax",@progbits
    .LCOLDB0:
        .section    .text.startup,"ax",@progbits
    .LHOTB0:
        .p2align 4,,15
        .globl  main
        .type   main, @function
    main:
    .LFB34:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        xorl    %r9d, %r9d
        movl    $-1, %r8d
        movl    $33, %ecx
        movl    $3, %edx
        movl    $4096, %esi
        xorl    %edi, %edi
        call    mmap
        movb    $48, (%rax)
        call    fork
    .L2:
        jmp .L2     ← ここで無限ループ
        .cfi_endproc
    .LFE34:
        .size   main, .-main
        .section    .text.unlikely
    .LCOLDE0:
        .section    .text.startup
    .LHOTE0:
        .ident  "GCC: (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413"
        .section    .note.GNU-stack,"",@progbits
    
[/code]

少なくとも `volatile` などを追加しないと、意図したとおりには動きません。

[code]

        volatile char *s;
        s = (volatile char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    
[/code]

このように修正したとしても、特にシングルスレッドな実行環境ではパイプを使った場合に比べ性能が落ちるのではないかと思います。両プロセスとも OS
にプリエンプトされるまで無駄にループが回り続け、タスクスイッチすることがないからです。




プロセス間通信という意味では、 `*(s+1) = 'a';` や `*s = '0';` や `*s = '1';`
をした時点で、それは即座に相手にも見えるようになります (キャッシュ構成の影響などで遅れることもありますが)。

コードの意図としては、親プロセスと子プロセスの間で `*(s+1)` を使って `'a'` を送信した・受信したという確認のやりとりのつもりで、 `*s`
に `'0'` や `'1'` を読み書きしてるのではと想像します。

このコードの別の問題点は、最適化つきでコンパイルするとまともに動かないことでしょう。次のようにコンパイルしたら fork()
の後に無限ループする結果になってしまいました。

[code]

    $ gcc -S -O3 a.c -o -
        .file   "a.c"
        .section    .text.unlikely,"ax",@progbits
    .LCOLDB0:
        .section    .text.startup,"ax",@progbits
    .LHOTB0:
        .p2align 4,,15
        .globl  main
        .type   main, @function
    main:
    .LFB34:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        xorl    %r9d, %r9d
        movl    $-1, %r8d
        movl    $33, %ecx
        movl    $3, %edx
        movl    $4096, %esi
        xorl    %edi, %edi
        call    mmap
        movb    $48, (%rax)
        call    fork
    .L2:
        jmp .L2     ← ここで無限ループ
        .cfi_endproc
    .LFE34:
        .size   main, .-main
        .section    .text.unlikely
    .LCOLDE0:
        .section    .text.startup
    .LHOTE0:
        .ident  "GCC: (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413"
        .section    .note.GNU-stack,"",@progbits
    
[/code]

少なくとも `volatile` などを追加しないと、意図したとおりには動きません。

[code]

        volatile char *s;
        s = (volatile char *)mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    
[/code]

このように修正したとしても、特にシングルスレッドな実行環境ではパイプを使った場合に比べ性能が落ちるのではないかと思います。両プロセスとも OS
にプリエンプトされるまで無駄にループが回り続け、タスクスイッチすることがないからです。

