← home

unix shared memory example

The simple act of acquiring a piece of shared memory through the SYSV SHM API is fiendishly tedious. This page used to have “simple” in the title, as in, a simple way to do this, but it was a bad title because there really is no simple way, if you're using the raw system call API. But this an example, at least.

If you want to use multiple blocks of shared memory in a single process then you might need to pass a different id to ftok() for each one. I used the same block every time so I had no need for that.

In any case, the main entry point is open_shm(). It returns a block of shared memory of the specified size. If justreading is 1 and there's been data written to the block of shared mem, then you access what has already been written. Otherwise, you create a new one with undefined contents.

Once you get your memory block successfully returned to you by open_shm(), you can treat it like any other block of memory that you'd get from e.g. malloc().

/* This code written by Nick Welch , 2006.
 *
 * This code is in the public domain
 * and is provided AS IS, with NO WARRANTY. */

static key_t get_shm_key(void)
{
    /* both arbitrary */
    const char * KEY_PATH = "/dev/null";
    const char KEY_ID = 0xc4; 

    key_t key = ftok(KEY_PATH, KEY_ID);
    if (key == (key_t)-1)
        DIE("ftok");

    return key;
}

static int get_shm_min(void)
{
    struct shminfo info;
    if((shmctl(0, IPC_INFO, (struct shmid_ds *)(void *)&info)) == -1)
        DIE("shmctl (shminfo)");
    return info.shmmin;
}

static void * open_shm(size_t bytes, int justreading)
{
    void * shm;
    key_t key = get_shm_key();

    int id; 

    if(!justreading)
    {
        /* we need to delete it in case we need to allocate more memory this
         * time than last time.  if we try that without first deleting it, we
         * will get EINVAL.
         */

        id = shmget(key, get_shm_min(), 0644|IPC_CREAT);

        if(id == -1)
            DIE("shmget (first call)");

        shm = shmat(id, NULL, 0);
        if (shm == NULL)
            DIE("shmat(first call)");

        if(shmctl(id, IPC_RMID, NULL) == -1)
            DIE("shmctl (marking shm segment for removal)");

        if(shmdt(shm) == -1)
            DIE("shmdt (detaching shm segment to delete it)");
    }

    id = shmget(key, bytes, justreading ? 0 : 0644|IPC_CREAT);
    if(id == -1)
        DIE("shmget (second call)");

    shm = shmat(id, NULL, 0);
    if (shm == NULL)
        DIE("shmat (second call)");

    return shm;
}

Nick Welch <nick@incise.org> · github