24627


下記のコードは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]


CC BY-SA 3.0
2016-05-07T03:07:24.663
24624
2016-07-20T11:56:45.557
5246
question
c
Cでmmapを使ったプロセス間通信


プロセス間通信という意味では、 `*(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
にプリエンプトされるまで無駄にループが回り続け、タスクスイッチすることがないからです。


CC BY-SA 3.0
2016-05-07T04:18:44.687
24627
2016-05-07T05:03:24.530
2016-05-07T05:03:24.530
9873
9873
24624
answer


参照先はタイトルなどからもわかりますように、頻繁にプロセス間通信が行われており、その際のシステムコールが実行時間に対して支配的となるような環境でのパフォーマンスチューニングです。

コードにある`while`ループは[スピンロック](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%94%E3%83%B3%E3%83%AD%E3%83%83%E3%82%AF)と呼ばれ、マルチプロセッサ環境でなおかつ非常に頻繁にプロセス間通信が行われその際のロック時間がシステムコールに対して十分短いことが期待できる場合には有効です。例えば挙げられたコードであれば２プロセスを１００万回往復しています。  
逆にシングルプロセッサ環境だったり、yaegashiさんも指摘されているようにプロセス間通信の頻度が低くロック待ち時間が長くなる場合は、無駄なループを回すことによりCPU負荷を上げるだけで非効率となります。

ですので、無条件に適用できるパフォーマンスチューニングではないことを認識してください。


CC BY-SA 3.0
2016-05-07T05:54:18.627
24629
2016-05-07T05:54:18.627
4236
24624
answer