IPC over shared memory segment - memcpy visibility

时间:2013-07-01 08:40:10

标签: c++ ipc shared-memory volatile memcpy

我用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()。

0 个答案:

没有答案