我正在开发一个写入/读取器lock_free队列,该队列放置在共享内存中,该内存将由两个不同的Linux进程打开。这两个进程都使用MAP_SHARED打开shm。
队列看起来像这样:
typedef stuct {
unsigned char Data[ 256 ];
} Element_Type;
typedef struct {
unsigned int Snd_Cnt;
unsigned int Rcv_Cnt;
Element_Type Elems[ 100 ];
} Queue_Type;
OBS:两个Linux进程都打开相同的共享内存,并将其视为指向Queue_Type的指针。我们说指针是Shm_p。
编写者的Linux进程是这样的:
Tmp_Snd_Cnt = (Shm_p->Snd_Cnt + 1u) % 100;
__atomic_load(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
memcpy(&Shm_p->Elems[ Tmp_Snd_Cnt ].Data[ 0 ], pointer_to_real_data, size_of_real_data <= 256);
__atomic_store(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_RELEASE);
}
读者的Linux进程是这样的:
Tmp_Rcv_Cnt = Shm_p->Rcv_Cnt;
__atomic_load(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
pointer_to_real_data = &Shm_p->Elems[ Tmp_Rcv_Cnt ].Data[ 0 ];
Tmp_Rcv_Cnt = (Tmp_Rcv_Cnt + 1u) % 100;
__atomic_store(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_RELEASE);
}
我的问题是,您认为种族和东西之类的方法存在任何问题吗?
我所看到的是以下内容:
有时,读取器会获得pointer_to_real_data指向未更新的数据。这表明GCC已将memcpy放置在atomic_store之后(即,编译时指令重新排序)。为了减轻这种情况,我在编写器代码中的原子存储之前放置了asm volatile("" : : : "memory")
。读者得到了正确的数据。
为了使事情变得更加陌生,我取消了asm的可变性并再次进行编译。 代码开始起作用。 (在再次测试之前,我已经清除了shm)。
因此,很明显,在第一种情况下,当读者获取未更新的数据时,编译器在没有asm volatile的情况下做了一些奇怪的事情。可能是什么?
现在,为了安全起见,我将asm volatile保存在其中,以指示编译器不要对代码重新排序。
谢谢。
最新编辑(为了减轻在存储Rcv_Cnt之后读取器变慢的情况下覆盖指标指针数据的问题):
typedef stuct {
unsigned char Data[ 256u ];
} Base_Element_Type;
typedef stuct {
unsigned int Idx;
Base_Element_Type Base_Elems[ 2u ];
} Element_Type;
typedef struct {
unsigned int Snd_Cnt;
unsigned int Rcv_Cnt;
Element_Type Elems[ 100u ];
} Queue_Type;
作者代码:
Tmp_Snd_Cnt = (Shm_p->Snd_Cnt + 1u) % 100u;
__atomic_load(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
Elem_p = &Shm_p->Elems[ Tmp_Snd_Cnt ];
memcpy(&Elem_p->Base_Elems[ Elem_p->Idx ].Data[ 0u ], pointer_to_real_data, size_of_real_data <= 256u);
__atomic_store(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_RELEASE);
}
阅读器代码:
Tmp_Rcv_Cnt = Shm_p->Rcv_Cnt;
__atomic_load(&Shm_p>Snd_Cnt, &Tmp_Snd_Cnt, __ATOMIC_ACQUIRE);
if (Tmp_Snd_Cnt != Tmp_Rcv_Cnt) {
Elem_p = &Shm_p->Elems[ Tmp_Rcv_Cnt ];
pointer_to_real_data = &Elem_p->Base_Elems[ Elem_p->Idx ].Data[ 0u ];
Elem_p->Idx = (Elem_p->Idx + 1u) % 2u;
Tmp_Rcv_Cnt = (Tmp_Rcv_Cnt + 1u) % 100u;
__atomic_store(&Shm_p>Rcv_Cnt, &Tmp_Rcv_Cnt, __ATOMIC_RELEASE);
}
这应该有效...对吧!