Search code examples
cipcshared-memoryvolatile

Shared-memory through shm_open and mmap: how do I avoid thread or process local caches when reading from my shared-memory?


I'm using shm_open + mmap to write a consumer and a producer that exchange data through shared-memory. The consumer is in one process and the producer is in another process. All good, but how can I be sure that my consumer is not going to keep reading cached memory (thread/process local cache) instead of the latest contents written by the producer?

Below my consumer C code:

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define STORAGE_ID "/SHM_TEST"
#define STORAGE_SIZE 32

int main(int argc, char *argv[])
{
    int res;
    int fd;
    char data[STORAGE_SIZE];
    pid_t pid;
    void *addr;
    
    pid = getpid();
     
    // get shared memory file descriptor (NOT a file)
    fd = shm_open(STORAGE_ID, O_RDONLY, S_IRUSR | S_IWUSR);
    if (fd == -1)
    {
        perror("open");
        return 10;
    }
     
    // map shared memory to process address space
    addr = mmap(NULL, STORAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
    {
        perror("mmap");
        return 30;
    }
     
    // place data into memory
    memcpy(data, addr, STORAGE_SIZE);
    
    printf("PID %d: Read from shared memory: \"%s\"\n", pid, data);
     
    return 0;
}

Solution

  • how can I be sure that my consumer is not going to keep reading cached memory (thread/process local cache) instead of the latest contents written by the producer?

    By synchronizing access to the shared memory with an appropriate synchronization object (which might itself live in the shared memory, though there are other options). @AndrewHenle discussed this in comments. A process-shared mutex would be the natural choice, because you are very likely going to want to use a (process shared) condition variable along with it, which will require a mutex.

    You need to apply appropriate synchronization to have any kind of useful program semantics, and if you do apply appropriate synchronization then that takes care of any caching / memory-ordering issues too. Thus, even if you're "doing one step at a time", as you put it in comments, ensuring proper synchronization and ensuring visibility of writes are not separate steps.