Example C++ Code for Using Shared Memory Conforming to the POSIX Standard, Essentials Only!

Introduction

After searching the net for exactly this, an example of C++ code for using shared memory conforming to the POSIX standard but stripped down to the mere essentials, I had to do it myself. A big help was Logan Chien‘s website, although his code was written in C only. So I had to convert it. You find Logans’s nicely elaborated and well explained stuff here:

http://logan.tw

What the Code Example Does

It takes information via stdin and thus can be fed by piping stuff into it. This information can be text, that’s the purpose I’ve intended: Take a small or even bigger textfile and pipe it into the sender program, and have it printed out by the receiver program. Example:

cat textfile | ./sender

Then launch

./receiver

That’s all, because I wanted a simple example of how to get started with shared memory.

Short Explanation

Both sender and receiver use protocol.h to share information about the memory segment: Its specific name and size.

The include statements are exactly what is needed, nothing more. The functions used and (more or less) specific to shared memory are:

shm_open()
ftruncate()
mmap()
munmap()
shm_unlink()

As you can see, you don’t need ftok() which is mandatory when you do it the System V way! There are basically two steps to claim a shared memory segment and to give it back to the kernel: shm_open() and shm_unlink().

After having successfully claimed the memory you truncate it to the size you need with ftruncate(). This command can be used the whole time while you have access to the shared memory. And although you might think otherwise, it can be used to enlarge and to shrink the segment!

To get access to the segment you’ll use mmap(), when finished munmap().

To me, the POSIX method seems to be easier to understand and thus easier to use than the System V method.

The Code

The shared protocol.h is as follows:

#ifndef PROTOCOL_H
#define PROTOCOL_H

#define NAME "/shmem-example"

#define SIZE 128000

#endif

The sender.cpp program looks like this:

#include "protocol.h"

#include                      // Standard IO
#include                      // File control
#include <sys/mman.h>         // Memory mapping
#include                      // Needed for ftruncate()

int main() {
  int fd = shm_open(NAME, O_CREAT | O_EXCL | O_RDWR, 0600);
  if (fd < 0) {
    perror("shm_open()");
    return EXIT_FAILURE;
  }

  ftruncate(fd, SIZE);

  char *data =
      (char *)mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

  fread(data, 128000, 1, stdin);
  std::cout << data << "\n\n";
  std::cout << "Sender mapped address: " << &data << "\n";

  munmap(data, SIZE);

  close(fd);

  return EXIT_SUCCESS;
}

The receiver.cpp program is this:

#include "protocol.h"

#include 
#include 
#include <sys/mman.h>
#include              // Needed for close()

int main() {
  int fd = shm_open(NAME, O_RDONLY, 0666);
  if (fd < 0) {
    perror("shm_open()");
    return EXIT_FAILURE;
  }

  char *data =
      (char *)mmap(0, SIZE, PROT_READ, MAP_SHARED, fd, 0);

  std::cout << data << "\n\n";
  std::cout << "receiver mapped address: " << &data << "\n";

  munmap(data, SIZE);

  close(fd);

  shm_unlink(NAME);

  return EXIT_SUCCESS;
}

How to Compile

The compiler commands are as follows:

g++ sender.cpp   -std=c++17 -I/usr/include -Wall -lrt -o sender
g++ receiver.cpp -std=c++17 -I/usr/include -Wall -lrt -o receiver

Roundup

The code presented above is obviously just a starter to get the hang of it, from here you can work your way up to stuff which is much more exciting than this first step 🙂

Happy coding!