4

プログラムがあります (Ubuntu 12.04 LTS、シングルコア プロセッサ):

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>

int main(){

    mode_t mode = S_IRUSR | S_IWUSR;
    int i = 0, fd, pid;
    unsigned char pi1 = 0x33, pi2 = 0x34;
    if((fd = open("res", O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0){

        perror("open error");
        exit(1);
    }



    if((pid = fork()) < 0){

       perror("fork error");
       exit(1);
    }

    if(pid == 0) {


       if(write(fd, &pi2, 1) != 1){

           perror("write error");
           exit(1);
       }  
    }else{

       if(write(fd, &pi1, 1) != 1){

           perror("write error");
           exit(1);
       }  
    }

    close(fd);
    return 0;
}

アイデアは、ファイルを書き込み用に開き、次に fork することです。両方のプロセスのレコード合計が存在する位置。奇妙なことは、プログラムを実行すると、ファイル「res」への出力が一定ではないことです。私は激怒し、次に 34、次に 4、そして 3 になりました。(結局のところ、ポジションが共有されている場合、結論は 34 または 43 のいずれかでなければなりません。)

私の疑いでは、彼が書き込む位置を見つけたときに、書き込み関数でプロセスが中断されます。

4

5 に答える 5

3

fork() で複数のプロセスを生成する場合、それらがどの順序で実行されるかを知る方法はありません。オペレーティング システムのスケジューラが決定します。

したがって、複数のプロセスが同じファイルに書き込むことは、災害のレシピです。

2 つの数字の 1 つが省略されることがある理由については、write は最初にデータを書き込み、次にファイル ポインタをインクリメントします。ファイル位置が更新される前に2番目のスレッドが書き込むように、スレッド制御がまさにその瞬間に変更される可能性があると思います。そのため、他のプロセスが書き込んだばかりのデータを上書きします。

于 2012-09-06T13:04:49.373 に答える
2

あなたのプログラムを数回実行しましたが、結果は "34" または "43" です。だから私はシェルスクリプトを書いています

#!/bin/bash
for i in {1..500}
    do
            ./your_program
            for line in $(cat res) 
            do
                    echo "$line" 
            done
    done

、プログラムを 500 回実行します。ご覧のとおり、「3」または「4」が数回発生します (500 で 20 回)。これをどのように説明できますか? 答えは、子プロセスを fork() すると、子プロセスは同じファイル記述とファイル状態構造 (現在のファイル オフセットを持つ) を共有するということです。通常、プロセスは最初にオフセット=0 を取得し、最初のバイトを書き込み、オフセット=1; 他のプロセスはオフセット=1 を取得し、2 番目のバイトを書き込みます。しかし、場合によっては、親プロセスがファイル状態構造体からオフセット=0 を取得し、子プロセスが同時にオフセット=0 を取得すると、プロセスが最初のバイトを書き込み、他のプロセスが最初のバイトを上書きします。結果は "3" または "4" になります (親が最初に書き込むか、子を書き込むかによって異なります)。どちらもファイルの最初のバイトを書き込むためです。

フォークとオフセット、こちらをご覧ください

于 2012-09-06T15:34:06.023 に答える
1

これが私が起こっていると思うことです。

  • あなたopenはファイルとあなたfork
  • 親が最初に実行されます (子が最初に実行された場合、同様のことが発生する可能性があります)
    • 親が書き込ん3で終了する
    • 親は端末の制御プロセスだったので、カーネルはフォアグラウンド グループのすべてのメンバーに SIGHUP を送信します。
  • のデフォルトのアクションはSIGHUPプロセスを終了することで、子プロセスは黙って終了します

これをテストする簡単な方法は、スリープを追加することです。

sleep(5); /* Somewhere in the child, right before you write. */

子プロセスが即座に停止することがわかります。書き込みは決して実行されません

これをテストする別の方法は、 fork する前にSIGHUP,を無視することです:

sigignore(SIGHUP);  /* Define _XOPEN_SOURCE 500 before including signal.h. */
/* You can also use signal(SIGHUP, SIG_IGN); */

プロセスが両方の数字をファイルに書き込むことがわかります。


上書き仮説はありそうもない。の後、両方のプロセスが、ファイル オフセットも含むシステム全体のテーブルfork内の同じファイル記述子へのリンクを共有します。

于 2012-09-06T13:58:31.330 に答える
1

基準は言う

{PIPE_BUF} バイト以下の書き込み要求は、同じパイプで書き込みを行う他のプロセスからのデータとインターリーブされません。

リンク - http://pubs.opengroup.org/onlinepubs/009696699/functions/write.html

書き込みの原子性は、PIPE_BUF 以下のパイプへの書き込みの場合にのみ保証され、通常のファイルには指定されていないため、それを想定することはできません。

したがって、この場合、競合状態が発生しているため、一部の実行ではデータが正しくありません。(私のシステムでも、数千回の実行後に発生しています)。

これを解決するには、ミューテックス/セマフォ/その他のロックプリミティブの使用を検討する必要があると思います。

于 2012-09-06T19:31:28.637 に答える
0

fork() が正確に必要ですか? fork() は、異なるメモリ空間 (ファイル記述子など) を持つ異なるプロセスを作成します。多分pthreadsはあなたに合っていますか?pthread の場合、すべてのプロセスで同じ fd を共有します。とにかく、プロジェクトでミューテックスを使用することを真剣に検討する必要があります。

于 2012-09-06T13:04:55.797 に答える