Applying C - Memory Mapped Files
Written by Harry Fairhead   
Tuesday, 26 December 2023
Article Index
Applying C - Memory Mapped Files
Memory Mapping Files
Shared Memory
Semaphore Locking

Shared Memory

You can use memory mapped files to allow processes to share memory - threads (see Chapter 12) share memory by default. The idea is much like pipes. You create a special file which is then associated with an area of memory using mmap as if it was a real file. Other processes can then open the same file and mmap it and use it to communicate.

To do this you need the shm_open function:

int fd = shm_open("filename",flags ,mode);

which creates a special file called filename, just like the usual open function. Once you have the file descriptor you have to use:

ftruncate(fd, size);

to set the file to a fixed size. Next you can use mmap to map the file into memory and start using it.

When you have finished with the memory mapping you can unmap it:

munmap(memptr, size);

You also need to close the file descriptor. If you leave the file in the filing system then it can be used again without being created. Alternatively you can unlink the file, after closing it, using:

shm_unlink("filename");

Here is a simple writer example:

#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/stat.h>       
#include <fcntl.h>  
#include <unistd.h>
#include <sys/types.h>
#include <inttypes.h>
int main(int argc, char** argv) {
    int value = 0x55555555;
    int fd = shm_open("BackingFile", O_RDWR |
O_CREAT, 0644); ftruncate(fd, 1000); uint8_t *memptr = mmap(NULL, 1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); for (int i = 0; i < 10; ++i) { value = ~value; memcpy(memptr,&value,4); sleep(1); } munmap(memptr, 1000); close(fd); shm_unlink("BackingFile"); return (EXIT_SUCCESS); }

 

You need to add the rt library to the linker to make this work.

This alternatively writes 0x55555555 and 0xAAAAAAAAA to the shared memory. Notice the use of memcpy to copy the four bytes in the int to the start of shared memory.

A reading program is just as simple:

#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/stat.h>       
#include <fcntl.h>  
#include <unistd.h>
#include <sys/types.h>
#include <inttypes.h>
int main(int argc, char** argv) {
    int value = 0x55555555;
    int fd = shm_open("BackingFile", O_RDWR, 0644);
    uint8_t *memptr = mmap(NULL, 1000, PROT_READ | 
                         PROT_WRITE, MAP_SHARED, fd, 0);
    for (int i = 0; i < 10; ++i) {      
        memcpy(&value,memptr,4);
        printf("%x\n", value);
        fflush(NULL);
        sleep(1);
    }
    munmap(memptr, 1000);
    close(fd);
    shm_unlink("BackingFile");
    return (EXIT_SUCCESS);
}

Notice that in this case the shm_open doesn't create the file, it simply opens it and it also doesn't have to set it to a size using ftruncate. You need to remember to add the rt library to the linker to make this work.

If you run the writer and then the reader you should see the pattern of values printed correctly. Notice that neither program has any error checks and neither makes use of locking to avoid the writer or reader being interrupted in the middle of an update. Given we are only writing 4 bytes this is unlikely but not impossible.

It is probably better to use pointers to the shared memory rather than using functions such as memcpy as this is more like how you work with memory created by malloc. For example:

uint32_t *memptr = mmap(NULL, 1000, PROT_READ |
PROT_WRITE, MAP_SHARED, fd, 0); int *value =memptr;

and in the read loop:

printf("%x\n", *value);

but notice if you use locking then you need to lock access to the shared memory. The lock time is reduced if you use local variables and only transfer them to the shared memory after their value has been computed.



Last Updated ( Wednesday, 27 December 2023 )