mmap_reader.c

DownloadView Raw

/**
 * mmap_reader.c
 * 
 * Demonstrates the process for setting up a shared region of memory, then
 * attempts to read messages from it that are enclosed in 'message' structs.
 * It's important to note that this is simply a proof of concept; polling the
 * shared memory every second for a new message is not efficient or safe (in
 * terms of race conditions, etc).
 *
 * If you are on a system with /dev/shm, check this location for the shared
 * object file once the program has started.
 *
 * We also try to be reasonably complete by shutting things down and cleaning up
 * when the user presses Ctrl+C.
 *
 * Compile (Linux): gcc -lrt -Wall -g mmap_reader.c -o reader
 * Compile (Mac): gcc -Wall -g mmap_reader.c -o reader
 * Run: ./reader shm_obj_name
 */

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

int g_shm_fd;
void *g_block;
char *g_shm_name;

struct message {
    unsigned int id;
    pid_t from_pid;
    char msg[128];
};

void close_handler(int signo)
{
    printf("Cleaning up shared object\n");

    if (munmap(g_block, sizeof(struct message)) == -1) {
        perror("munmap");
    }

    if (shm_unlink(g_shm_name) == -1) {
        perror("shm_unlink");
    }

    if (close(g_shm_fd) == -1) {
        perror("close");
    }

    printf("Goodbye!\n");
    exit(0);
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("Usage: %s shm_obj_name", argv[0]);
        return 1;
    }

    signal(SIGINT, close_handler);
    signal(SIGTERM, close_handler);

    g_shm_name = argv[1];

    size_t size = sizeof(struct message);

    int shm_fd = shm_open(g_shm_name, O_CREAT | O_RDWR, S_IRWXU);
    if (shm_fd == -1) {
        perror("shm_open");
        return 1;
    }

    if (ftruncate(shm_fd, size) == -1) {
        perror("ftruncate");
        return 1;
    }

    g_block = mmap(
            NULL, /* Address (we use NULL to let the kernel decide) */
            size, /* Size of memory block to allocate */
            PROT_READ | PROT_WRITE, /* Memory protection flags */
            MAP_SHARED, /* Type of mapping */
            shm_fd, /* file descriptor */
            0 /* offset to start at within the file */);

    if (g_block == MAP_FAILED) {
        perror("mmap");
    }

    int last_msg = 0;
    struct message *msg = (struct message *) g_block;
    while (true) {
        if (msg->id == last_msg) {
            sleep(1);
            continue;
        }

        printf("Received message %d from PID %d: '%s'\n",
                msg->id,
                msg->from_pid,
                msg->msg);
        last_msg = msg->id;
    }

    return 0;
}