Logo  

CS471/571 - Operating Systems

Lesson 10

POSIX shared memory objects

  • Reading: man 7 shm_overview

POSIX shared memory objects are normally file-backed, as a file in the file-system is used as a named data object for disparate processes to map into their individual address spaces for sharing. Non-file backed anonymous mappings can be shared between processes after a fork().

shm_open() - create/open a POSIX shared memory object

#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */

int shm_open(const char *name, int oflag, mode_t mode);

Link with -lrt

int shm_unlink(const char *name);

shm_open() is virtually identical in function to the normal open() system call except that the file created will be created in the RAM drive /dev/shm (a virtual file-system where files created within it exist entirely in memory and are not stored on any disk,) which can then be used as a file backing for a mmap'ed memory segment to be mapped into one or more processes address space. If the files permissions are set to allow another process to access the file, then the same file can be shm_opened by another process and mmap'ed into its address space, creating a shared memory segment between the two or more processes. shm_unlink() is used to delete the file created by shm_open(), which should be done sometime after the last process that will map the file has access to it.

ftruncate() - truncate a file to a specified length

#include <unistd.h>
#include <sys/types.h>

int ftruncate(int fd, off_t length);

The ftruncate() function can be used to size a file (by its file-descriptor) to a desired size. To increase or decrease the size of a file-backed mmap'ed memory region, you must increase or decrease the size of the file (this should be done before the mapping.) You can obviously also do this by writing data to the file.

On success 0 is returned, -1 on failure and errno is set.

Example:

int sfd = shm_open("foo", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (sfd < 0) {
  perror("shm_open");
}

// Make the file 4096 bytes in size:
if (ftruncate(sfd, 4096) < 0) {
  perror("ftruncate");
}

//...
shm_unlink("foo");

mmap() - Map files or devices into memory

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

The mmap() system call creates a mapping in the processes memory of length number of bytes starting at the optionally given address addr (use NULL to let the kernel decide where to place the mapping.) If the memory region to be mapped is a file, then fd should be the file descriptor of the file with offset set to the offset within the file to begin the mapping at. If the mapping is to be anonymous then, fd should be set to -1 and offset to 0.

prot is the binary OR of one or more of the following memory access permissions:

PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.


flags is one of MAP_SHARED or MAP_PRIVATE for either a shared memory mapping or a copy-on-write private mapping (note that changes made using MAP_PRIVATE are not written back to any file.) Additional flags can be binary OR'ed the above, such as MAP_ANONYMOUS which allows creating a non-file backed anonymous mapping.

The return value for mmap is the address of the memory region on success or MAP_FAILED ( (void *)-1 ) on failure.

Example:

// Would map the file opened by shm_open() above:
void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);

munmap() - unmap a mapped memory region

int munmap(void *addr, size_t length);

munmap removes part of all of a mapped region. addr must be a multiple of the page size, however length does not need to be. Closing the file that is the file-backing for a memory region does not unmap the memory region.

Example:

munmap(ptr, 4096);

Shared memory example:

Writer:

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

#define SHM_FILE    "shm-file"
#define K   1024

struct message {
  int clr2send;
  char mbuf[K];
};

/**
 * Compile with: gcc -o writer writer.c -lrt
 */

int main(void)
{
  char mesg[K];
  struct message *m;

  int sfd = shm_open(SHM_FILE, O_RDWR | O_CREAT | O_TRUNC, 0666);
  if (sfd < 0) {
    perror("shm_open");
    return 1;
  }

  if (ftruncate(sfd, sizeof(struct message)) < 0) {
    perror("ftruncate");
    close(sfd);
    shm_unlink(SHM_FILE);
    return 1;
  }

  m = mmap(NULL, sizeof(struct message), PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);
  if (m == MAP_FAILED) {
    perror("mmap");
    close(sfd);
    shm_unlink(SHM_FILE);
    return 1;
  }

  m->clr2send = 0;

  while (1) {
    printf("? ");
    if (fgets(mesg, K, stdin) == NULL) break;
    while (m->clr2send == 0) usleep(100000);
    strncpy(m->mbuf, mesg, K);
    m->clr2send = 0;
  }

  close(sfd);
  shm_unlink(SHM_FILE);
  return 0;
}

Reader

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

#define SHM_FILE    "shm-file"
#define K   1024

struct message {
  int clr2send;
  char mbuf[K];
};

/**
 * Compile with: gcc -o reader reader.c -lrt
 */

int main(void)
{
  char mesg[K];
  struct message *m;

  int sfd = shm_open(SHM_FILE, O_RDWR, 0666);
  if (sfd < 0) {
    perror("shm_open");
    return 1;
  }

  m = mmap(NULL, sizeof(struct message), PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);
  if (m == MAP_FAILED) {
    perror("mmap");
    close(sfd);
    shm_unlink(SHM_FILE);
    return 1;
  }

  while (1) {
    m->clr2send++;
    while (m->clr2send > 0) usleep(100000);
    printf("%s", m->mbuf);
  }

  close(sfd);
  return 0;
}