Notes for Shared Memory

Shared memory allows multiple processes to share virtual memory space. One process creates or allocates the shared memory segment with size and access permissions. shared memory is identified by a unique identifier that shmget system call returns. The process then attaches the shared segment, causing it to be mapped into its current data space. Once created, and if permissions permit, other processes can attach to the shared memory segment and map it into their data space. Each process accesses the shared memory relative to its attachment address. For each process involved, the mapped memory appears to be no different from any other of its memory addresses. A process can detach from it. Normally the process that created the segment is the owner, but it can grant ownership to another process. The owner is responsible for removing it.

Creating a Shared Memory segment.

shmget system call is used to 1:) create the shared memory segment and generate the share memory data structure or 2:) to gain access to an existing segment.
    int shmget(key_t key, int size, int shmflg);
where size is the size in bytes of the shared memory segment. If shmget is used to access an existing segment, then it can be 0.
key could be
1:) IPC_PRIVATE,
2:) or is not associated with an existing shared memory identifier and IPC_CREAT
3:) or is not associated with an existing shared memory identifier and IPC_CREAT along with IPC_EXCL flag have been set. With IPC_CREAT and IPC_EXCL set, the user can be assured of creating a unique shared memory segment without inadvertently gaining access to a preexisting segment.
When shmget is successful, it returns an integer shared memory identifier. At creation time, the system data structure shmid_ds is generated and initialized. It contains lot of maintain information.

struct shmid_ds {
    struct ipc_perm shm_perm;        /* operation permission struct     */
    size_t shm_segsz;                /* size of segment in bytes        */
    __time_t shm_atime;              /* time of last shmat()            */
    unsigned long int __unused1;
    __time_t shm_dtime;              /* time of last shmdt()            */
    unsigned long int __unused2;
    __time_t shm_ctime;              /* time of last change by shmctl() */
    unsigned long int __unused3;
    __pid_t shm_cpid;                /* pid of creator                  */
    __pid_t shm_lpid;                /* pid of last shmop               */
    shmatt_t shm_nattch;             /* number of current attaches      */
    unsigned long int __unused4;
    unsigned long int __unused5;
  };

 

Shared Memory Control.

shmctl system call permits the user to perform a number of generalized control operations on an existing shared memory segment, and on the system shared memory data structure.
   int shmctl(int shmid, int cmd, struct shmid_ds *buf);
The 2nd argument, cmd, specifies the operations shmctl is to perform. They can be IPC_STAT, IPC_SET, IPC_RMID, SHM_LOCK and SHM_UNLOCK.
The 3rd argument is a reference to a structure of the type shmid_ds.

Shared Memory Operations.

void *shmat(int shmid, const void*shmaddr, int shmflg);
int shmdt ( const void *shmaddr);

shmat is used to attach (map) the referenced shared memory segment into the calling process's data segment. Normally we set the 2nd argument, shmaddr, 0, the system picks the attachment address. The third argument, shmflg, is used to specify the access permissions for the shared memory segment and to request special attachment conditions, such as an aligned address or a read-only segment. By default, attached segments are accessible for reading and writing. If needed, the SHM_RDONLY flag can be bitwise ORed with the shmflg value to indicate a read-only segment. There is no flag to specify a write-only memory segment. The SHM_RND flag is used to specify whether or not the attachment address should be aligned on a page boundary.

shmdt system call has one argument, shmaddr, which is a reference to an attached memory segment. If shmdt is successful in detaching the memory segment, it returns a value of 0. It also sets shm_atime to the current time, shm_lpid to the ID of the calling process, and decrements shm_nattch by one. If shm_nattch becomes 0 and the memory segment is marked for deletion by the operating system, it is removed. If the shmdt call fails, it returns a value of -1 and sets errno.

Sample Code:
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#define SHM_SIZE 30
using namespace std;
extern int etext, edata, end;
int  main( ) {
     int    shmid;
     char   c, *shm, *s;
     if ((shmid=shmget(IPC_PRIVATE,SHM_SIZE,IPC_CREAT|0660))< 0) {
       perror("shmget fail");
        return 1;
     }
     if ((shm = (char *)shmat(shmid, 0, 0)) == (char *) -1) {
         perror("shmat : parent");
        return 2;
     }
    cout << "Addresses in parent"  << endl;
    cout << "shared mem: " << hex << int(shm) << " etext: "
           << &etext << " edata: "  << &edata
           << " end: " << &end << endl << endl;
     s = shm;                             // s now references shared mem
     for (c='A'; c <= 'Z'; ++c)           // put some info there
        *s++ = c;
        *s='\0';                            // terminate the sequence
        cout << "In parent before fork, memory is: " << shm << endl;
        switch (fork( )) {
        case -1:
          perror("fork");
          return 3;
        default:
          wait(0);                          // let the child finish
          cout << "In parent after fork, memory is : " << shm << endl;
          cout << "\nParent removing shared memory" << endl;
          shmdt(shm);
          shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
          break;
        case 0:
          cout << "In child after fork, memory is  : " << shm << endl;
          for ( ; *shm; ++shm)              // modify shared memory
            *shm += 32;
          shmdt(shm);
          break;
        }
       return 0;
}

iclx012$ ./a.out
Addresses in parent
shared mem: 9556c000 etext: 0x401116 edata: 0x501788 end: 0x5018a8

In parent before fork, memory is: ABCDEFGHIJKLMNOPQRSTUVWXYZ
In child after fork, memory is : ABCDEFGHIJKLMNOPQRSTUVWXYZ
In parent after fork, memory is : abcdefghijklmnopqrstuvwxyz

Parent removing shared memory

Using a File as Shared Memory

The user also can map a file to a process's virtual memory address by using mmap system call. Unlike memory, the contents of a file are nonvolatile.

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

The first, start, is the address for attachment. Normally you set to 0 and let the system to choose for you.
The second, length, is a number of bytes that will extend beyond the end of the mapped file.
prot is used to set the type of access for the segment. It can be PROT_READ, PROT_WRITE, PROT_EXEC and PROT_NONE
The fouth argument, flags, specifies the type of mapping. It can be MAP_SHARED, MAP_PRIVATE and MAP_FIXED. It also supports some other flags, such as MAP_GROWSDOWN, MAP_EXECUTABLE, MAP_DENYWRITE, MAP_NORESERVE and MAP_LOCKED et
The fifth argument, fd, is a valid open file descriptor.
The sixth argument, offset, is used to set the starting position for the mapping.

When the process terminates, the system will automatically unmap a region. But you can explicitly unmap pages of memory.

int munmap(void *start, size_t length);  

It passes the starting address of the memory mapping (argument start and the size of the mapping (argument length. If the call is successful, it returns a value of 0. Future references to unmapped addresses generate a SIGSEGV which is sig:11.  

Sampel code:
//#define _GNU_SOURCE
#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
sing namespace std;
int main(int argc, char *argv[]){
  int          fd, changes, i, random_spot, kids[2];
  struct stat  buf;
  char         *the_file, *starting_string="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  if (argc != 3) {
    cerr << "Usage " << *argv << " file_name #_of_changes" << endl;
    return 1;
  }
  if ((changes = atoi(argv[2])) < 1) {
    cerr << "# of changes < 1" << endl;
    return 2;
  }
  if ((fd = open(argv[1], O_CREAT | O_RDWR, 0666)) < 0) {
    perror("file open");
    return 3;
  }
  write(fd, starting_string, strlen(starting_string));
                                       // Obtain size of file
  if (fstat(fd, &buf) < 0) {
    perror("fstat error");
    return 4;
  }
                                       // Establish the mapping
  if ((the_file = (char *) mmap(0, (size_t) buf.st_size,
                     PROT_READ | PROT_WRITE, MAP_SHARED,
                     fd, 0)) == (void *) - 1) {
    perror("mmap failure");
    exit(5);
  }
  for (i = 0; i < 2; ++i)
    if ((kids[i] = (int) fork()) == 0)
       while (1) {
          cout << "Child " << getpid() << " finds: " << the_file << endl;
          sleep(1);
       }
  srand((unsigned) getpid());
  for (i = 0; i < changes; ++i) {
     random_spot = (int) (rand() % buf.st_size);
     *(the_file + random_spot) = '*';
     sleep(1);
  }
  cout << "In parent, done with changes" << endl;
  for (i = 0; i < 2; ++i)
    kill(kids[i], 9);
  cout << "The file now contains: " << the_file << endl;
  return 0;
}
[iclx012]~/private> ./a.out /tmp/my 10
Child 7139 finds: ABCDEFGHIJKLMNOPQRSTUVWXY*
Child 7140 finds: ABCDEFGHIJKLMNOPQRSTUVWXY*
Child 7139 finds: ABCDEFGHIJKLMNOPQRSTU*WXY*
Child 7140 finds: ABCDEFGHIJKLMNOPQRSTU*WXY*
Child 7139 finds: ABCDEFGHIJKLMNO*QRSTU*WXY*
Child 7140 finds: ABCDEFGHIJKLMNO*QRSTU*WXY*
Child 7139 finds: A*CDEFGHIJKLMNO*QRSTU*WXY*
Child 7140 finds: A*CDEFGHIJKLMNO*QRSTU*WXY*
Child 7139 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7140 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7139 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7140 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7139 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7140 finds: A*CDEFGHIJKLMNO*QRS*U*WXY*
Child 7139 finds: A*CDE*GHIJKLMNO*QRS*U*WXY*
Child 7140 finds: A*CDE*GHIJKLMNO*QRS*U*WXY*
Child 7139 finds: A*CD**GHIJKLMNO*QRS*U*WXY*
Child 7140 finds: A*CD**GHIJKLMNO*QRS*U*WXY*
Child 7139 finds: A*CD**GHIJKLMNO*QRS***WXY*
Child 7140 finds: A*CD**GHIJKLMNO*QRS***WXY*
In parent, done with changes
The file now contains: A*CD**GHIJKLMNO*QRS***WXY*

Summary of Shared Memory

Share memory provides an efficient way of communication via the sharing the data that resides in memory.

The Shared memory can be accessed in non-serial (random) manner. It means that semaphore or other synchronization method must be used to coordinate access to shared memory segment.

shmget is used the create and gain access to an existing segment.

shmctl is used to obtain the status of a memory segment, set permissions, and remove a shared memory segment.

shmat and shmdt are used to attach and detach shared memory segments.

mmap system call is used th map the virtual memory space of a process to a file. It is very useful since the data a in file, and it wont lose after process exists.

This is a personal WEB site developed and maintained by an individual and not by Seattle University. The content and link(s) provided on this site do not represent or reflect the view(s) of Seattle University. The individual who authored this site is solely responsible for the site's content. This site and its author are subject to applicable University policies including the Computer Acceptable Use Policy (www.seattleu.edu/policies).