我在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-&gt; heartbeat
将大于1,000,000,000,但很少和随机,
我会看到结果conf-&gt; heartbeat将小于1,000,000,000,
在shmem1.exe或shmem2.exe !!
如果只运行shmem1.exe,它总是打印1,000,000,000,我的问题是, 在shmem1.exe中导致conf-&gt; heartbeat =(840825951)的原因是什么?
更新:虽然不确定,但我想我弄清楚发生了什么,例如shmem1.exe运行10次,然后conf-&gt; heartbeat = 10,在这段时间shmem1.exe休息一下然后回来,shmem1.exe从共享内存读取并且conf-> heartbeat = 8,所以shmem1.exe将从8继续,为什么conf-&gt; heartbeat = 8?我认为这是因为shmem2.exe将共享内存数据更新为8,shmem1.exe在休息之前没有写回10共享内存....这只是我的理论...我不知道如何证明它!!
答案 0 :(得分: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都在运行,这意味着shmem1.exe可以在shmem2.exe读取和写入conf->heartbeat
之间多次递增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重新排序的情况下发生,只是安排疯狂。