2

RedHat 5.5で実行されている4.1.2でコンパイルされた2つのプログラムがあります。これは、次のように共有メモリshmem1.cをテストする簡単な作業です。

#define STATE_FILE "/program.shared"
#define  NAMESIZE 1024
#define   MAXNAMES 100
typedef struct
{
    char name[MAXNAMES][NAMESIZE];
    int heartbeat ;
    int iFlag ;
}  SHARED_VAR;

int main (void)
{
    int first = 0;
    int shm_fd;
    static SHARED_VAR *conf;

    if((shm_fd = shm_open(STATE_FILE, (O_CREAT | O_EXCL | O_RDWR),
                   (S_IREAD | S_IWRITE))) > 0 ) {
        first = 1; /* We are the first instance */
    }
    else if((shm_fd = shm_open(STATE_FILE, (O_CREAT | O_RDWR),
                    (S_IREAD | S_IWRITE))) < 0) {
        printf("Could not create shm object. %s\n", strerror(errno));
        return errno;
    }
    if((conf =  mmap(0, sizeof(SHARED_VAR), (PROT_READ | PROT_WRITE),
               MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {

        return errno;
    }
    if(first) {
        for(idx=0;idx< 1000000000;idx++)
        {
            conf->heartbeat = conf->heartbeat + 1 ;
        }
    }
    printf("conf->heartbeat=(%d)\n",conf->heartbeat) ;
    close(shm_fd);
    shm_unlink(STATE_FILE);
    exit(0);
}//main

そしてshmem2.cは次のようになります:

#define STATE_FILE "/program.shared"
#define  NAMESIZE 1024
#define   MAXNAMES 100

typedef struct
{
    char name[MAXNAMES][NAMESIZE];
    int heartbeat ;
    int iFlag  ;
}  SHARED_VAR;

int main (void)
{
    int first = 0;
    int shm_fd;
    static SHARED_VAR *conf;

    if((shm_fd = shm_open(STATE_FILE, (O_RDWR),
                    (S_IREAD | S_IWRITE))) < 0) {
        printf("Could not create shm object. %s\n", strerror(errno));
        return errno;
    }
    ftruncate(shm_fd, sizeof(SHARED_VAR));
    if((conf =  mmap(0, sizeof(SHARED_VAR), (PROT_READ | PROT_WRITE),
               MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
        return errno;
    }
    int idx ;
    for(idx=0;idx< 1000000000;idx++)
    {
        conf->heartbeat = conf->heartbeat + 1 ;
    }
    printf("conf->heartbeat=(%d)\n",conf->heartbeat) ;
    close(shm_fd);
    exit(0);
}

コンパイル後:

   gcc shmem1.c -lpthread -lrt -o shmem1.exe
   gcc shmem2.c -lpthread -lrt -o shmem2.exe

そして、2つの端末でほぼ同時に両方のプログラムを実行します:

   [test]$ ./shmem1.exe
   First creation of the shm. Setting up default values
   conf->heartbeat=(840825951)
   [test]$ ./shmem2.exe
   conf->heartbeat=(1215083817)

戸惑う!! shmem1.cは1,000,000,000回のループなので、840,825,951のような答えを得るにはどうすればよいでしょうか。

shmem1.exeとshmem2.exeをこのように実行すると、ほとんどの結果はconf-> heartbeatが1,000,000,000を超えますが、めったにランダムに、結果conf->heartbeatが
shmem1.exeまたはshmem2.exe !!

shmem1.exeのみを実行すると、常に1,000,000,000が出力されます。私の質問は、shmem1.exeでconf-> heartbeat =(840825951)が発生する理由は何ですか。

更新:わかりませんが、何が起こっているのか理解できたと思います。たとえば、shmem1.exeが10回実行された場合、conf-> heartbeat = 10、今回はshmem1.exeが休憩してから、shmem1に戻ります。 .exeは共有メモリから読み取られ、conf-> heartbeat = 8なので、shmem1.exeは8から続行されます。なぜ、conf-> heartbeat = 8なのですか?shmem2.exeが共有メモリデータを8に更新し、shmem1.exeが休憩する前に共有メモリに10を書き戻さなかったためだと思います....それは私の理論です...方法がわかりませんそれを証明するために!!

4

1 に答える 1

4

返される値は、共有メモリをアトミックにインクリメントしていないことを示しています。次のループ:

int idx ;
for(idx=0;idx< 1000000000;idx++)
{
    conf->heartbeat = conf->heartbeat + 1 ;
}

要約すると、次のようになります。

int idx ;
for(idx=0;idx< 1000000000;idx++)
{
    // read
    int heartbeat= conf->heartbeat;

    // write
    conf->heartbeat = heartbeat + 1 ;
}

読み取りコメントと書き込みコメントの間に、プロセスをスワップアウトして別のプロセスを実行させることができます。shmem1.exeとshmem2.exeの両方が実行されている場合はconf->heartbeat、shmem2.exeの読み取りと書き込みの間にshmem1.exeを何度もインクリメントしたりconf->heartbeat、その逆を行ったりできることを意味します。

一貫性のある更新が必要な場合は、プラットフォームのアトミックメモリ増分関数を使用する必要があります。これにより、読み取り/変更/書き込み操作によって、古い値が書き戻される可能性があるのではなく、常に値がインクリメントされることが保証されます。

たとえば、shmem1.exeとshmem2.exeの間に同期がない場合、shmem1.exeとshmem2.exeの両方が次の出力を出力するこの病的なケースが発生する可能性があります2

shmem1.exe: read 0
shmem2.exe: read 0
// shmemem2.exe goes to sleep for a loooong time
shmem1.exe: write 1
// ... shmem1.exe keeps running
shmem1.exe: write 999,999,999
// shmem2.exe wakes up
shmem2.exe write 1
shmem2.exe read 1
// shmem2.exe goes back to sleep
shmem1.exe read 1(!)
// shmem1.exe goes to sleep
// shmem2.exe wakes up
shmem2.exe write 2
shmem2.exe read 2
shmem2.exe write 3
// shmem2.exe continues, shmem1.exe stays asleep
shmem2.exe read 999,999,999
shmem2.exe write 1,000,000,000
// shmem2.exe goes to sleep, shmem1.exe wakes up
shmem1.exe write 2(!)
shmem1.exe read 2
shmem1.exe print 2
//shmem2.exe wakes up
shmem2.exe read 2
shmem2.exe print 2

これは、CPUを並べ替えることなく、狂気をスケジュールするだけで発生する可能性があります。

于 2012-12-12T04:08:22.630 に答える