C 测试共享内存,奇怪的事情发生了

C 测试共享内存,奇怪的事情发生了,c,linux,shared-memory,C,Linux,Shared Memory,我有两个在4.1.2中编译的程序,运行在RedHat 5.5中, 测试共享内存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 ma

我有两个在4.1.2中编译的程序,运行在RedHat 5.5中, 测试共享内存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
#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);
}
并使用2个终端几乎同时运行两个程序:

   [test]$ ./shmem1.exe
   First creation of the shm. Setting up default values
   conf->heartbeat=(840825951)
   [test]$ ./shmem2.exe
   conf->heartbeat=(1215083817)
我感到困惑!!既然shmem1.c是一个100000000次的循环,它怎么可能是 可能有840825951这样的答案吗

我以这种方式运行shmem1.exe和shmem2.exe,大多数结果都是conf->heartbeat 将大于100000000,但很少随机, 我将看到结果conf->heartbeat将小于100000000, 在shmem1.exe或shmem2.exe中

如果只运行shmem1.exe,它总是打印100000000,我的问题是, 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写回共享内存…这只是我的理论。。。我不知道如何证明它

返回的值表明您没有以原子方式增加共享内存。以下循环:

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

这可以在没有CPU重新排序的情况下发生,而只是进行疯狂的调度。

您返回的值表明您没有以原子方式增加共享内存。以下循环:

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

这可以在没有CPU重新排序的情况下发生,而只是疯狂地调度。

在第一个程序中定义idx的地方?请粘贴完整的代码。您可能应该使用一种机制来保护您的共享内存不受多次同时访问的影响。您需要互斥或信号量:在第一个程序中定义idx的位置?请粘贴完整的代码。你可能应该使用一种机制来保护你的共享内存,防止多次同时访问。你需要互斥或信号量:谢谢,我明白你的意思了,在我将自旋锁添加到这两者之后,shmem1.exe或shmem2.exe end中的一个将获得2000000000,这是完美的,让我困惑的是,即使我没有对conf->heartbeat进行原子操作,shmem1.exe如何获得小于100000000的数字?我预计至少会超过100000000个asm volatile:::conf->heartbeat=conf->heartbeat+1之后的内存仍然会得到小于100000000的数字,但如果我添加asm volatilemfence:::内存,则在shmem1.exe和shmem2.exe中永远不会有小于100000000的答案,所以我想知道这是否是cpu重新排序的情况?@barfatchen,我用一个场景更新了我的答案,在这个场景下,你可以让shmem1.exe和shmem2.exe输出2。如果两个进程同时访问同一个内存并写入其中,难道不会发生崩溃吗?@MSN我同意你关于原子RWM的说法。除此之外,我认为为了让编译器完全意识到我们正在通过conf访问共享内存,我们需要将conf声明为volatile shared_VAR*;否则,编译器可能会对其进行过多优化,而不会尝试读取或写入主内存。当我不通过可变变量读取时,我遇到了这样的问题。谢谢,我明白你的意思了,在我将自旋锁添加到两者之后,shmem1.exe或shmem2.exe end中的一个将获得2000000000,这是完美的,让我困惑的是,即使我没有conf->heartbeat的原子操作,shmem1.exe如何获得少于1000000000的数字?我预计至少会超过100000000个asm volatile:::conf->heartbeat=conf->heartbeat+1之后的内存仍然会得到小于100000000的数字,但如果我添加asm volatilemfence:::内存,则在shmem1.exe和shmem2.exe中永远不会有小于100000000的答案,所以我想知道这是否是cpu重新排序的情况?@barfatchen,我用一个场景更新了我的答案,在这个场景下,你可以让shmem1.exe和shmem2.exe输出2。如果两个进程同时访问同一个内存并写入其中,难道不会发生崩溃吗?@MSN我同意你关于原子RWM的说法。除此之外,我认为为了让编译器完全意识到我们正在通过conf访问共享内存,我们需要将conf声明为volatile shared_VAR*;否则,编译器可能会对其进行过多优化,而不会尝试读取或写入主内存。当我不通过可变变量读取时,我遇到了这样的问题。
int idx ;
for(idx=0;idx< 1000000000;idx++)
{
    // read
    int heartbeat= conf->heartbeat;

    // write
    conf->heartbeat = heartbeat + 1 ;
}
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