mario::konrad
programming / C++ / sailing / nerd stuff
Multiprocessing: Tutorial 03
© 2002 / Mario Konrad

What is ‘Shared Memory’?

Unix processes run in protected memory spaces, this means that no process is able to access another process’ memory. If you like two or more processes to share some data, you have to advice the operting system to create a memory space which all processes have access to. This “feature” is called shared memory (shared between the processes).

As mentioned before, the shared memory is handles by the operating system. This means you cannot allocate memory going the usual way using malloc and free, you have to use some system calls instead. Consider the shared memory as a resource like any of the other resouced controlled by the operating system. You have to reserve the resource in the beginning and you have to release the resource after you used it.

Do not worry, there are not many system calls you have to know. But there’s another thing you might have to know. Multiple processes are running concurrently. Shared memory is not protected with a mutual exclusion. You have to make sure which process is accessing when the shared memory. In many cases there’s no need for such a synchronisation, it depends on your application.

The Program

This program has two processes (parent and child), that access properties in a shared memory. To demonstrate that the memory is really shared, the child process will alter the data of the properties. Both processes display the content of the properties.

Walkthrough

The number of header files to include is very minimalistic:

#include <stdio.h>
#include <string.h>
#include <sys/shm.h>

A few symbols we need later.

#define KEY_LENGTH 32
#define VALUE_LENGTH 32
#define MAX_PROPERTIES 1

The following structure is not really needed for the handling of the shared memory. This is application specific and is used to show a possible use of shared memory.

The structure represents a property, a unique key and a value. In this case both are strings.

typedef struct
{
    char key[KEY_LENGTH];
    char value[VALUE_LENGTH];
} property_t;

Some global variables. The variable magic_key is to identify the shared memory. This is an arbitrary but unique number.

property_t * property;
int magic_key = 9876;
int id = -1;

Both, parent and child process, are created within the function main. This is just to simplify the code. As you may see, we will use very few variables.

int main(int argc, char ** argv)
{
    struct shmid_ds shm_desc;
    int pid;
    int i;

First we need to allocate a memory space, that is shared between the processes. This is done using the system call shmget(2). Please read the man page to get more specific information.

Note: do not use malloc and free to allocate shared memory, it won’t work!

    /* create shared memory space */
    id = shmget(magic_key,
        sizeof(property_t) * MAX_PROPERTIES,
        IPC_CREAT | IPC_EXCL | 0600);

If the allocation of the shared memory failed, shmget will return -1. In this case the program should stop.

    if (id < 0)
    {
        fprintf(stderr, "%s: error: create shared memory space\n",
            argv[0]);
        exit(-1);
    }

Next, we’ll need to fold the shared memory space into this process. Please read the man page of shmat(2) for further information.

    /* attach shared memory space */
    property = shmat(id, 0, 0);
    if (property <= 0)
    {
        fprintf(stderr, "%s: error: attach to shared memory space\n",
            argv[0]);
        exit(-2);
    }

From now on, we’re able to access the shared memory space as we do as normal memory space allocated using malloc. For example: we could fill the shared memory with some data.

    /* fill properties */
    strcpy(property[0].key, "key 0");
    strcpy(property[0].value, "value 0");

To prove that two different processes, running in different protected memory spaces, are able to access the shared memory we need to create a child process.

    /* create child process */
    pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "%s: error\n", argv[0]);

If the creation of the child process failed, we must clean up the shared memory, since it is handled by the operating system. This is done with the system call shmctl(2).

        if (shmctl(id, IPC_RMID, &shm_desc) < 0)
        {
            fprintf(stderr, "%s: error\n", argv[0]);
            exit(-4);
        }
        exit(-3);
    }
    else if (pid == 0)
    {

The code of the child process contains nothing spectacular. It is a simple loop, in which the shared memory gets written every two seconds.

        /* child code */
        for (i = 5; i; --i)
        {
            printf("%s: child: property 0: %s = %s\n",
                argv[0], property[0].key, property[0].value);
            sprintf(property[0].key, "value %d\0", i);
            usleep(2000000L);
        }
    }
    else
    {

The code of the parent process is nothing special as well. Just read the shared memory every two seconds and print the content.

        /* parent code */
        for (i = 5; i; --i)
        {
            printf("%s: parent: property 0: %s = %s\n",
                argv[0], property[0].key, property[0].value);
            usleep(2000000L);
        }

The parent process waits for the child process to terminate and then releases the allocated, shared memory.

        /* wait for child process to terminate and free resources */
        waitpid(pid);
        if (shmctl(id, IPC_RMID, &shm_desc) < 0)
        {
            fprintf(stderr, "%s: error\n", argv[0]);
            exit(-4);
        }
    }

That’s it, let’s get out of the program.

    return 0;
}

Compile and Execute

Compilation and execution is very easy since you don’t need any special library.

To compile use the following command:

$ gcc -o process3 process3.c

To execute is just as easy as to compile:

$ ./process3

Excercises

Downloads

All source code files provided by this page is free to copy, modify, redistribute and use for any purpose. The tutorial is copyrighted by Mario Konrad.