CS 110 Arch/OS
SHARED MEMORY NOTES

Declarations and Creation

You need an integer ID for the shared memory. Also, you need a data structure to map onto the shared memory. Buffer struct is defined as:
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
}

//********************
int    bufferID;          //the shared mem ID for the buffer
buffer *theBuffer;        //pointer to the shared mem area
                          //mapped as a struct of type Buffer
//********************

Ask the OS for shared memory. First parameter is a key, shows up in ipcs calls. Second parameter is the size wanted. This can be determined by the function sizeof which will return the size of a structure. Third parameter is once again, the operation and the protections. Basically, you are asking the OS to create a shared memory area of a specific size, somewhere in memory that it the OS manages, but which is owned (belongs) to the creating process.


//********************
//get shared memory
if ( (bufferID = shmget( key, sizeof(buffer), (IPC_CREAT | 0644))) < 0 )
cerr << "cannot get shared memory" << endl;
//********************

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.
Also Note that the code above should do a better job of parsing any errors.

Attaching

Creating shared memory space is not good enough. You asked the OS for it, but when you want access to the shared memory, the process needs to make it look like a well known data structure. In IPCS terminology this is attaching the shared memory . Basically, you are asking the OS to change the relationship of the created shared memory to a data structure mapped into your address space. In particular treating it as a particular structure. shmget creates shared memory and shmat maps shared memory into a process' 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.

//********************
// attach the shared memory, by having theBuffer point to it.
if ((theBuffer =(buffer *) shmat( bufferID, (char *)0, 0 )) == (buffer *) -1)
{
  cerr << "main cannot attach shared memory" << endl;
}
//********************

Initialization & Using Shared Memory

Happens after some process asked the OS to create the shared memory area, and the shared memory area has been attached. In our case the pointer theBuffer points to the shared memory and allows the process to treat the shared memory as an instantiation of the struct Buffer.


//********************
//* initialize the buffer
theBuffer -> GEntries = 0;     //total number of people to enter buffer
theBuffer -> GExits = 0;       //total number of people to exit buffer

for( int i=0; ichair[i] = 0;
...etc
//********************

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 processes address space.
//********************
//* detach the buffer, all done with it
shmdt( (void *) theBuffer );
//********************

Returning Shared Memory

When all the processes are finished, then the shared memory should be returned to the OS (be nice).

//********************
//* return shared mem
shmctl( bufferID, IPC_RMID, NULL );
//********************

Keys

In both shmget and semget the first arg is a key. If every user is using the same key, either every user will get the same block of shared memory, or the call will fail. A way to avoid this situation:

//********************
while( (sem=semget(++semNum,numSem,(IPC_CREAT|IPC_EXCL|0644))) == -1 )
{
    if( errno == ENOSPC )
    {
      cerr << "Failed to allocate semaphore" << endl;
      return -1;              // Failed to allocate semaphore
    }
}
//********************

The IPC_EXCL|IPC_CREAT flags will cause the call to fail if a semaphore or shared memory associated with key, semNum already exists, otherwise the creation will happen. Note: semNum is a program global that is either initialized to 0 or to a random value, see ftok. Note: if you do not clean up your shared memory and semaphores after program execution, key conflicts are more likely to occur.

Dynamic Arrays and Shared Memory

In the following declaration the intent is to have State point to a dynamic array. For shared memory, a problem exists in that the size of Buffer does not include the shared memory, but only the integer pointer, State. This confuses shmget as documented above.
//********************
struct Buffer
{
   int *State;
};
//********************
shmget then becomes:
//********************
//get shared memory for a dynamic array of NumPhils integers
// size is determine by the number of bytes for the type NumPhils

if ( (BufferId = shmget( ++semNum, ((sizeof (int)) * NumPhils), (IPC_CREAT | 0644))) < 0 )
{
    cerr << "cannot get shared memory" << endl;
    exit (-1);
}
//********************
shmat then becomes:
//********************
// attach the shared memory, by having TheBuffer point to it, 
// where State is the pointer to the dynamic array
 
if ((TheBuffer -> State  = (int *) shmat( BufferId, (char *)0, 0 )) == (int *)
 -1)
{
   cerr << "main cannot attach shared memory" << endl;

}

Last modified September 27, 2001 by mike@cs.hmc.edu