【问题标题】:Testing shared memory ,strange thing happen测试共享内存,奇怪的事情发生
【发布时间】:2012-12-12 03:30:11
【问题描述】:

我有 2 个在 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

和 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 将小于 1,000,000,000 ,
在 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 写回共享内存......这只是我的理论......我不知道如何证明一下!!

【问题讨论】:

  • 你在第一个程序中定义 idx 的地方?请粘贴完整的代码。
  • 您可能应该使用一种机制来保护您的共享内存免受需要互斥锁或信号量的多个同时访问:stackoverflow.com/a/12468183/1634695

标签: c linux shared-memory


【解决方案1】:

您返回的值表明您没有自动增加共享内存。以下循环:

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 ;
}

在读取和写入 cmets 之间,可以换出一个进程以让另一个进程运行。如果 shmem1.exe 和 shmem2.exe 都在运行,这意味着您可以让 shmem1.exe 在 shmem2.exe 读取和写入 conf-&gt;heartbeat 之间多次递增 conf-&gt;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 重新排序的情况下发生,只是疯狂的调度。

【讨论】:

  • 谢谢,我明白你的意思了,在我将自旋锁添加到两者之后,shmem1.exe 或 shmem2.exe 中的一个将得到 2,000,000,000,这是完美的,让我感到困惑的是,即使我没有对 conf->heartbeat 没有原子操作, shmem1.exe 如何获得小于 1,000,000,000 的数字?我预计至少超过 1,000,000,000
  • 我在 conf->heartbeat = conf->heartbeat + 1 之后添加 asm volatile("" ::: "memory") 仍然会得到小于 1,000,000,000 的数字,但是如果我添加 asm volatile(" mfence" ::: "memory") ,那么在 shmem1.exe 和 shmem2.exe 中永远不会有答案小于 1,000,000,000 ,所以我想知道这是否是 cpu 重新排序的情况?
  • @barfatchen,我用一个场景更新了我的答案,在这个场景下你可以让 shmem1.exe 和 shmem2.exe 输出 2。
  • 如果两个进程同时访问同一个内存并写入它,应该不会崩溃?
  • @MSN 我同意你所说的原子 RWM。除了这个问题,我想为了让编译器完全知道我们正在通过conf访问共享内存,我们需要将conf声明为volatile SHARED_VAR*;否则,编译器可能会对其进行过多优化,以至于它不会尝试读取或写入主内存。当我不通过volatile variable 阅读时遇到了这样的问题。 link
猜你喜欢
  • 2016-03-19
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 2013-04-01
  • 2010-11-07
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
相关资源
最近更新 更多