CS 110, Arch and Operating Systems
Shared Memory Notes

  • Declaring and creating shared memory
  • Attaching shared memory
  • Initializating & using shared memory
  • Detaching shared memory
  • Returning shared memory
  • Why isn't my pointer shared?
  • Declaring and Creating Shared Memory

    You will usually need a data structure to map onto your shared memory. For example, suppose that the struct named Buffer is defined as follows (this buffer would be useful in the barbershop project):

        struct Buffer
        {
          int GEntries;     // Number of grunts to enter the shop
          int GExits;       // Number of grunts to exit the shop
          int NEntries;     // Number of noncoms to enter the shop
          int NExits;       // Number of noncoms to exit the shop
          int OEntries;     // Number of officers to enter the shop
          int OExits;       // Number of officers to exit the shop
          int BarberChair;  // 0, empty, 1 occupied
        }
    

    To ask the OS for shared memory, you need to specify the size of the shared area you want. This can be determined by using the sizeof keyword. The first and third arguments to shmget are as for semget: a key from ftok and a set of flags. Here is an example:

        int bufferID;       // The ID of the shared buffer
    
        // Get the shared memory
        int key = ftok(...);
        bufferID = shmget(key, sizeof (Buffer), IPC_CREAT | 0644);
        if (bufferID < 0) {
            cerr << "cannot create shared memory: " << strerror(errno) << endl;
            exit(1);
        }
    

    Note that after a successful shmget, the process has access to a shared memory area of the requested size. The process only knows that area by an integer value, bufferID.

    Attaching Your Shared Memory

    Creating shared memory space is not good enough. You asked the OS for it, but you don't actually have access to it. In addition, when you want access to the shared memory, you usually need to make it look like a well-known data structure. In IPCS terminology all of this is called attaching the shared memory. Basically, you are asking the OS to set up a relationship between the created shared memory and a data structure mapped into your address space. Shmget creates shared memory and shmat maps it into a process's address space. Depending on the order of actions, shared memory can be created and attached before any forks, and thus since children inherit, children will automatically have the shared memory mapped to a common data structure. This is usually the most convenient approach.

    Here is an example of how to attach shared memory:

        Buffer* sharedData; // Pointer to where the OS put the shared stuff
    
        sharedData = (Buffer*)shmat(bufferID, (char *)0, 0);
        if (sharedData == (Buffer*)-1) {
            cerr << "cannot attach shared memory: " << strerror(errno) << endl;
            exit(1);
        }
    
    Note failure is indicated by returning a dummy pointer equal to -1; this is the reason for the odd typecast in the if test.

    Initializating & Using Shared Memory

    When you first create and attach shared memory, it has not been initialized. In our case the pointer sharedData now points to the shared memory, and you can use it like any other memory of type Buffer. (In C, you need to call it struct Buffer.) So you can initialize things like this:

        sharedData->GEntries = 0;
        sharedData->GExits = 0;
        for (int i = 0;  i < NUM_CHAIRS;  i++)
            sharedData->chair[i] = 0;
        // etc.
    

    Using the shared memory is similar. Just remember that it's shared: any change you make will be immediately visible in other processes that share the same memory.

    Detaching Shared Memory

    Once a process is finished with the shared memory, then the process should detach it. Detaching does not destroy the shared memory area, but rather removes any mapping to that process's address space:

        shmdt((void *)sharedData);
        // sharedData is now an invalid pointer, just as if you had deleted it
    

    Returning Shared Memory

    When all the processes are finished, then the shared memory should be returned to the OS:

        shmctl(bufferID, IPC_RMID, (struct shmid_ds*)NULL);
    

    Why Isn't My Pointer Shared?

    Often, you will want to share some structure that contains a pointer to data that you also would like to share. For example:

        struct Tricky {
            int count;
            double* usefulStuff;
        };
    
        // ...
    
        Tricky* sneaky = (Tricky*)shmat(...);
        sneaky->count = 10;
        sneaky->usefulStuff = new double[50];
    
    The above code won't do what you want because new doesn't know anything about shared memory. So although sneaky->count is shared among all processes, the 50 doubles you allocated are private.

    There are two solutions to the problem. The first is to create a separate shared-memory area to hold your dynamically allocated array. You then write:

        Tricky* sneaky = (Tricky*)shmat(...);
        sneaky->count = 10;
        int usefulStuffID = shmget(..., sizeof(double) * 50, ...);
        sneaky->usefulStuff = (double*)shmat(usefulStuffID, ...);
    
    Note that you have to multiple sizeof(double) by 50 to get the size of your second shared-memory area, because sizeof(double) is the size of only one double, not 50. If you forget to multiply, your code will work fine for small array sizes but will mysteriously fail when you get up to some moderately large boundary.

    The second approach, which is beyond the scope of this FAQ, is to create a single large shared-memory area that has enough space for both Tricky and the array of doubles. Then you use some clever pointer manipulations to set up the usefulStuff pointer. The second approach is more efficient (because it doesn't waste precious shared-memory IDs) but ugly.

    Last modified February 24, 2002 by geoff@cs.hmc.edu