5.1 Shared Memory

One of the simplest interprocess communication methods is using shared memory. Shared memory allows two or more processes to access the same memory as if they all called malloc and were returned pointers to the same actual memory. When one process changes the memory, all the other processes see the modification.

5.1.1 Fast Local Communication

Shared memory is the fastest form of interprocess communication because all processes share the same piece of memory. Access to this shared memory is as fast as accessing a process's nonshared memory, and it does not require a system call or entry to the kernel. It also avoids copying data unnecessarily.

Because the kernel does not synchronize accesses to shared memory, you must provide your own synchronization. For example, a process should not read from the memory until after data is written there, and two processes must not write to the same memory location at the same time. A common strategy to avoid these race conditions is to use semaphores, which are discussed in the next section. Our illustrative programs, though, show just a single process accessing the memory, to focus on the shared memory mechanism and to avoid cluttering the sample code with synchronization logic.

5.1.2 The Memory Model

To use a shared memory segment, one process must allocate the segment. Then each process desiring to access the segment must attach the segment. After finishing its use of the segment, each process detaches the segment. At some point, one process must deallocate the segment.

Understanding the Linux memory model helps explain the allocation and attachment process. Under Linux, each process's virtual memory is split into pages. Each process maintains a mapping from its memory addresses to these virtual memory pages, which contain the actual data. Even though each process has its own addresses, multiple processes' mappings can point to the same page, permitting sharing of memory. Memory pages are discussed further in Section 8.8, "The mlock Family: Locking Physical Memory," of Chapter 8, "Linux System Calls."

Allocating a new shared memory segment causes virtual memory pages to be created. Because all processes desire to access the same shared segment, only one process should allocate a new shared segment. Allocating an existing segment does not create new pages, but it does return an identifier for the existing pages. To permit a process to use the shared memory segment, a process attaches it, which adds entries mapping from its virtual memory to the segment's shared pages. When finished with the segment, these mapping entries are removed. When no more processes want to access these shared memory segments, exactly one process must deallocate the virtual memory pages.

All shared memory segments are allocated as integral multiples of the system's page size, which is the number of bytes in a page of memory. On Linux systems, the page size is 4KB, but you should obtain this value by calling the getpagesize function.

5.1.3 Allocation

A process allocates a shared memory segment using shmget ("SHared Memory GET"). Its first parameter is an integer key that specifies which segment to create. Unrelated processes can access the same shared segment by specifying the same key value. Unfortunately, other processes may have also chosen the same fixed key, which could lead to conflict. Using the special constant IPC_PRIVATE as the key value guarantees that a brand new memory segment is created.

Its second parameter specifies the number of bytes in the segment. Because segments are allocated using pages, the number of actually allocated bytes is rounded up to an integral multiple of the page size.

The third parameter is the bitwise or of flag values that specify options to shmget. The flag values include these:

·         IPC_CREAT— This flag indicates that a new segment should be created. This permits creating a new segment while specifying a key value.

·         IPC_EXCL— This flag, which is always used with IPC_CREAT, causes shmget to fail if a segment key is specified that already exists. Therefore, it arranges for the calling process to have an "exclusive" segment. If this flag is not given and the key of an existing segment is used, shmget returns the existing segment instead of creating a new one.

·         Mode flags— This value is made of 9 bits indicating permissions granted to owner, group, and world to control access to the segment. Execution bits are ignored. An easy way to specify permissions is to use the constants defined in <sys/stat.h> and documented in the section 2 stat man page. [1] For example, S_IRUSR and S_IWUSR specify read and write permissions for the owner of the shared memory segment, and S_IROTH and S_IWOTH specify read and write permissions for others.

[1] These permission bits are the same as those used for files. They are described in Section 10.3, "File System Permissions."

For example, this invocation of shmget creates a new shared memory segment (or access to an existing one, if shm_key is already used) that's readable and writeable to the owner but not other users.

int  segment_id  =  shmget  (shm_key,  getpagesize  (), 
                             IPC_CREAT  |  S_IRUSR  |  S_IWUSER); 

If the call succeeds, shmget returns a segment identifier. If the shared memory segment already exists, the access permissions are verified and a check is made to ensure that the segment is not marked for destruction.

5.1.4 Attachment and Detachment

To make the shared memory segment available, a process must use shmat, "SHared Memory ATtach." Pass it the shared memory segment identifier SHMID returned by shmget. The second argument is a pointer that specifies where in your process's address space you want to map the shared memory; if you specify NULL, Linux will choose an available address. The third argument is a flag, which can include the following:

·         SHM_RND indicates that the address specified for the second parameter should be rounded down to a multiple of the page size. If you don't specify this flag, you must page-align the second argument to shmat yourself.

·         SHM_RDONLY indicates that the segment will be only read, not written.

If the call succeeds, it returns the address of the attached shared segment. Children created by calls to fork inherit attached shared segments; they can detach the shared memory segments, if desired.

When you're finished with a shared memory segment, the segment should be detached using shmdt ("SHared Memory DeTach"). Pass it the address returned by shmat. If the segment has been deallocated and this was the last process using it, it is removed. Calls to exit and any of the exec family automatically detach segments.

5.1.5 Controlling and Deallocating Shared Memory

The shmctl ("SHared Memory ConTroL") call returns information about a shared memory segment and can modify it. The first parameter is a shared memory segment identifier.

To obtain information about a shared memory segment, pass IPC_STAT as the second argument and a pointer to a struct shmid_ds.

To remove a segment, pass IPC_RMID as the second argument, and pass NULL as the third argument. The segment is removed when the last process that has attached it finally detaches it.

Each shared memory segment should be explicitly deallocated using shmctl when you're finished with it, to avoid violating the systemwide limit on the total number of shared memory segments. Invoking exit and exec detaches memory segments but does not deallocate them.

See the shmctl man page for a description of other operations you can perform on shared memory segments.

5.1.6 An Example Program

The program in Listing 5.1 illustrates the use of shared memory.

Listing 5.1 (shm.c) Exercise Shared Memory
#include <stdio.h> 
#include <sys/shm.h> 
#include <sys/stat.h> 
int main () 
  int segment_id; 
  char* shared_memory; 
  struct shmid_ds shmbuffer; 
  int segment_size; 
  const int shared_segment_size = 0x6400; 
  /* Allocate a shared memory segment.  */ 
  segment_id = shmget (IPC_PRIVATE, shared_segment_size, 
                     IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); 
  /* Attach the shared memory segment.  */ 
  shared_memory = (char*) shmat (segment_id, 0, 0); 
  printf ("shared memory attached at address %p\n", shared_memory); 
  /* Determine the segment's size. */ 
  shmctl (segment_id, IPC_STAT, &shmbuffer); 
  segment_size  =               shmbuffer.shm_segsz; 
  printf ("segment size: %d\n", segment_size); 
  /* Write a string to the shared memory segment.  */ 
  sprintf (shared_memory, "Hello, world."); 
  /* Detach the shared memory segment.  */ 
  shmdt (shared_memory); 
  /* Reattach the shared memory segment, at a different address.  */ 
  shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0); 
  printf ("shared memory reattached at address %p\n", shared_memory); 
  /* Print out the string from shared memory.  */ 
  printf ("%s\n", shared_memory); 
  /* Detach the shared memory segment.  */ 
  shmdt (shared_memory); 
  /* Deallocate the shared memory segment.  */ 
  shmctl (segment_id, IPC_RMID, 0); 
  return 0; 

5.1.7 Debugging

The ipcs command provides information on interprocess communication facilities, including shared segments. Use the -m flag to obtain information about shared memory. For example, this code illustrates that one shared memory segment, numbered 1627649, is in use:

% ipcs -m 
------ Shared Memory Segments -------
key       shmid     owner     perms     bytes     nattch    status 
0x00000000 1627649   user    640       25600     0 

If this memory segment was erroneously left behind by a program, you can use the ipcrm command to remove it.

% ipcrm shm 1627649 

5.1.8 Pros and Cons

Shared memory segments permit fast bidirectional communication among any number of processes. Each user can both read and write, but a program must establish and follow some protocol for preventing race conditions such as overwriting information before it is read. Unfortunately, Linux does not strictly guarantee exclusive access even if you create a new shared segment with IPC_PRIVATE.

Also, for multiple processes to use a shared segment, they must make arrangements to use the same key.