我用C语言共享内存通信。
以下结构位于已分配段的最开头。
typedef struct {
int closedCount;
int shmId;
int semId;
int size;
volatile bool closed;
volatile unsigned int readCount;
volatile unsigned int writeCount;
volatile bool readBlocked;
volatile bool writeBlocked;
} T_IpcData;
我使用2个信号量 - 1个用于读者,1个用于写作。
Writer将数据放入共享内存,更新writeCount并通知读取器信号量。如果writeCount - readCount == size,则writer会在编写器信号量上等待。
Reader尝试使用memcpy从共享内存中读取writeCount - readCount字节,更新readCount并通知writer信号量。如果readCount == writeCount,读者会在读取器信号量上等待。
问题:有时(非常罕见)我的应用中出现错误,这是因为数据读取与写入不一致。
我在单CPU(8核)和双CPU(16核)机器上运行测试。操作系统 - Ubuntu。问题很少在两者上重现。
问题:memcpy提供的“更改可见性”的保证是什么?如何确保读者正确地看到在编写器线程中完成的所有修改?
谢谢!
更新(代码)
template <class T>
jlong Java_org_gridgain_grid_util_ipc_shmem_GridIpcSharedMemoryUtils_ReadShMem(
JNIEnv *env, jclass, jlong shMemPtr, T dest, jlong dOffset, jlong len, jlong timeout) {
T_IpcData *ipcData = (T_IpcData*) (((char *) shMemPtr) - BUF_OFFSET);
int unreadCnt = GetUnreadCount(ipcData);
while (unreadCnt == 0) {
if (unreadCnt == 0 && ipcData->closed) {
return -1;
}
// signal the other party, if it's blocked
if (ipcData->writeBlocked) {
if (__DEBUG) {
cerr << "Before write semaphore notification 1 [semId=" << ipcData->semId << "]\n" << flush;
}
SemNotify(env, ipcData->semId, SEM_WRITE, ipcData);
}
if (__DEBUG) {
cerr << "Before read semaphore wait [semId=" << ipcData->semId << "]\n" << flush;
}
ipcData->readBlocked = 1;
SemWait(env, ipcData->semId, SEM_READ, timeout, ipcData);
ipcData->readBlocked = 0;
unreadCnt = GetUnreadCount(ipcData);
}
int bytesRead = 0;
while (unreadCnt > 0 && bytesRead < len) {
int pos = ipcData->readCount % ipcData->size;
int len0 = (ipcData->size - pos < unreadCnt)? ipcData->size - pos: unreadCnt;
if (len0 > len - bytesRead) {
len0 = len - bytesRead;
}
RW::FromShMem(env, dest, dOffset + bytesRead , len0, (void*) (shMemPtr + pos));
ipcData->readCount += len0;
bytesRead += len0;
if (__DEBUG) {
cerr << "Before write semaphore notification 2 [semId=" << ipcData->semId << "]\n" << flush;
}
SemNotify(env, ipcData->semId, SEM_WRITE, ipcData);
unreadCnt = GetUnreadCount(ipcData);
}
return bytesRead;
}
template <class T>
jlong Java_org_gridgain_grid_util_ipc_shmem_GridIpcSharedMemoryUtils_WriteShMem(
JNIEnv *env, jclass clsName, jlong shMemPtr, T src, jlong sOffset, jlong len, jlong timeout) {
T_IpcData *ipcData = (T_IpcData*) (((char *) shMemPtr) - BUF_OFFSET);
int bytesWritten = 0;
while(bytesWritten < len) {
// Wait for reader.
int unreadCnt = GetUnreadCount(ipcData);
int pos = ipcData->writeCount % ipcData->size;
while (unreadCnt == ipcData->size) {
if (ipcData->closed) {
env->ThrowNew(env->FindClass(GRID_EXCEPTION), "Shared memory segment has been closed.");
return -1;
}
// signal the other party, if it's blocked
if (ipcData->readBlocked) {
SemNotify(env, ipcData->semId, SEM_READ, ipcData);
}
ipcData->writeBlocked = 1;
SemWait(env, ipcData->semId, SEM_WRITE, timeout, ipcData);
ipcData->writeBlocked = 0;
unreadCnt = GetUnreadCount(ipcData);
}
int len0 = ipcData->size - ((pos > unreadCnt)? pos : unreadCnt);
if (len0 > len - bytesWritten) {
len0 = len - bytesWritten;
}
if (ipcData->closed) {
env->ThrowNew(env->FindClass(GRID_EXCEPTION), "Shared memory segment has been closed");
return -1;
}
RW::ToShMem(env, src, sOffset + bytesWritten, len0, (void*) (shMemPtr + pos));
ipcData->writeCount += len0;
bytesWritten += len0;
SemNotify(env, ipcData->semId, SEM_READ, ipcData);
}
return GetUnreadCount(ipcData);
}
SemWait()调用semop(),SemNotify()调用semctl()。