IPC-共享内存 (七猫的藏经阁 )

来源:互联网 发布:w7网络打印机怎么设置 编辑:程序博客网 时间:2024/04/29 08:54

一共有三种,常用的是两种:ACE里封装了SRV的和POSIX的,名字叫ACE_Shared_Memory_MM(POSIX)和ACE_Shared_Memory_SV,他们都是从ACE_Shared_Memory继承而来,所以使用的时候一般这样用:

ACE_Shared_Memory *shm_client = new ACE_Shared_Memory_MM (shm_key);

 ACE_Shared_Memory *shm_client=new ACE_Shared_Memory_SV (SHM_KEY, SHMSZ);

Chapter 3. Sharing Memory Between Processes

There are three families of functions that let you create a segment of memory and share it among the address spaces of multiple processes. All produce the same result: a segment of memory that can be accessed or updated asynchronously by more than one process. You have to design protocols that prevent one process from changing shared data while another process is using the same data (seeChapter 4, “Mutual Exclusion”).

This chapter covers three major topics:

  • “POSIX Shared Memory Operations” describes the POSIX functions for sharing memory.

  • “IRIX Shared Memory Arenas” describes IRIX shared memory arenas.

  • “System V Shared Memory Functions” describes the SVR4 functions.

Overview of Memory Sharing

The address space is the range of memory locations that a process can use without an error. (The concept of the address space is covered in detail inChapter 1, “Process Address Space.”) In a pthreads program, all threads use the same address space and share its contents. In a program that starts multiple, lightweight processes withsproc(), all processes share the same address space and its contents. In these programs, the entire address space is shared automatically.

Normally, distinct processes (created by the fork() or exec() system calls) have distinct address spaces, with no writable contents in common. The facilities described in this chapter allow you to define a segment of memory that can be part of the address space of more than one process. Then processes or threads running in different address spaces can share data simply by referring to the contents of the shared segment in memory.

Shared Memory Based on mmap()

The basic IRIX system operation for shared memory is the mmap() function, with which a process makes the contents of a file part of its address space. The fundamental uses ofmmap() are covered under “Mapping Segments of Memory” (see also the mmap(2) reference page). When two or more processes map the same file into memory with the MAP_SHARED option, that single segment is part of both address spaces, and the processes can update its contents concurrently.

The POSIX shared memory facility is a simple, formal interface to the use of mmap() to share segments. The IRIX support for shared arenas is an extension ofmmap() to make it simpler to create a shared allocation arena and coordinate its use. The SVR4 facilities do not directly usemmap() but have similar results.

Sharing Memory Between 32-Bit and 64-Bit Processes

Larger Silicon Graphics systems support both 32-bit and 64-bit programs at the same time. It is possible for a memory segment to be mapped by programs using 32-bit addresses, and simultaneously mapped by programs that use 64-bit addresses. There is nothing to prevent such sharing.

However, such sharing can work satisfactorily only when the contents of the shared segment include no addresses at all. Pointer values stored by a 64-bit program can't be used by a 32-bit program and vice versa. Also the two programs will disagree about the size and offset of structure fields when structures contain addresses. For example, if you initialize an allocation arena withacreate() from a 64-bit program, a 32-bit program calling amalloc() on that same arena will almost certainly crash or corrupt the arena pointers.

You can use POSIX shared memory, SVR4 shared memory, or basic mmap() to share a segment between a 32-bit and a 64-bit program, provided you take pains to ensure that both programs view the data contents as having the same binary structure, and that no addresses are shared. You cannot use an IRIX shared memory arena between 32-bit and 64-bit programs at all, because theusinit() function stores addresses in the arena.

POSIX Shared Memory Operations

Shared-memory support specified by POSIX is based on the functions summarized inTable 3-1.

Table 3-1. POSIX Shared Memory Functions

Function Name

Purpose and Operation

mmap(2)

Map a file or shared memory object into the address space.

shm_open(2)

Create, or gain access to, a shared memory object.

shm_unlink(2)

Destroy a shared memory object when no references to it remain open.

The use of mmap() is described at length under “Mapping Segments of Memory”. In essence, mmap() takes a file descriptor and makes the contents of the described object accessible as a segment of memory in the address space. In IRIX, a file descriptor can describe a disk file, or a device, or a special pseudo-device such as /dev/kmem. Thus mmap() can make a variety of objects part of the address space. POSIX adds one more type of mappable object, a persistent shared segment you create using theshm_open() function.

Creating a Shared Object

The shm_open() function is very similar to the open() function and takes the same arguments (compare theshm_open(2) and open(2) reference pages). The arguments are as follows:

path

Name of object, a character string in the form of a file pathname.

oflag

Option flags, detailed in the reference page and discussed in following text.

mode

Access mode for the opened object

In order to declare shm_open() and its arguments you need to include bothsys/mman.h and fcntl.h header files.

Shared Object Pathname

The POSIX standard says that a shared object name has the form of a file pathname, but the standard leaves it “implementation defined” whether the object is actually a file or not. In the IRIX implementation, a shared memory object is also a file. The pathname you specify for a shared memory object is interpreted exactly like the pathname of a disk file that you pass toopen(). When you create a new object, you also create a disk file of the same name. (See“POSIX IPC Name Space”.)

You can display the size, ownership, and permissions of an existing shared segment usingls -l. You can dump its contents with a command such as od -X. You can remove it withrm.

Shared Object Open Flags

The flags you pass to shm_open() control its actions, as follows:

O_RDONLY

Access can be used only for reading.

O_RDWR

Access can be read-write (however, you can enforce read-only access when callingmmap()).

O_CREAT

If the object does not exist, create it.

O_TRUNC

If the object does exist and O_RDWR is specified, truncate it to zero length.

O_EXCL

If the object does exist and O_CREAT is specified, return the EEXIST error code.

The flags have the same meaning when opening a disk file with open(). However, a number of other flags allowed byopen() are not relevant to shared memory objects.

You can use the combination O_CREAT+O_EXCL to ensure that only one process initializes a shared object.

Shared Object Access Mode

The access mode that you specify when creating an object governs the users and groups that can open the object later, exactly as with a disk file.

Using the Shared Object File Descriptor

The value returned by shm_open() is a file descriptor and you can use it as such; for example you can apply thedup() function to make a copy of it. You can also use it as an argument tofcntl(), but most of the features of fcntl() are irrelevant to a shared memory object. (See thedup(2) and fcntl(2) reference pages.)

Using a Shared Object

In order to use a shared object, your program first opens it with shm_open(), then maps it into memory withmmap(). The arguments to mmap() include

  • the file descriptor for the shared object

  • the size of the memory segment

  • access protection flags

The returned value is the base address of the segment in memory. You can then use it like any block of memory. For example, you could create an allocation arena in the segment using theacreate() function (see the amalloc(3) reference page). For more on the use ofmmap(), read “Segment Mapping Function mmap()” and “Mapping a File for Shared Memory”.

Example Program

The program in Example 3-1 allows you to experiment with shm_open() and mmap() from the command line. The program accepts the following command-line arguments:

path

The pathname of a shared memory segment (file) that exists or that is to be created.

-p perms

The access permissions to apply to a newly-created segment, for example -p 0664.

-s bytes

The initial size at which to map the segment, for example -s 0x80000.

-c

Use the O_CREAT flag with open(), creating the segment if it doesn't exist.

-x

Use the O_EXCL flag with open(), requiring the segment to not exist.

-t

Use the O_TRUNC flag with open(), truncating the file to zero length.

-r

Use the O_RDONLY flag with open() and PROT_READ with mmap(). If this option is not used, the program uses O_RDWR withopen() and PROT_READ, PROT_WRITE, PROT_AUTOGROW with mmap().

-w

Wait for keyboard input before exiting, allowing you to run other copies of the program while this one has the segment mapped.

To create a segment named /var/tmp/test.seg, use a command such as

shm_open -c -x -p 0644 -s 0x80000 /var/tmp/test.seg

To attach that segment read-only and then wait, use the command

shm_open -r -w /var/tmp/test.seg

From a different terminal window, enter the command

shm_open /var/tmp/test.seg

In the original window, press <Enter> and observe that the value of the first word of the shared segment changed during the wait.

Example 3-1. POSIX Program to Demonstrate shm_open()

/*||  Program to test shm_open(3).||      shm_open    [-p <perms>] [-s <bytes>] [-c] [-x] [-r] [-t] [-w] <path>||      -p <perms>  access mode to use when creating, default 0600||      -s <bytes>  size of segment to map, default 64K||      -c          use O_CREAT||      -x          use O_EXCL||      -r          use O_RDONLY, default is O_RDWR||      -t          use O_TRUNC||      -w          wait for keyboard input before exiting||      <path>      the pathname of the queue, required*/#include <sys/mman.h>   /* shared memory and mmap() */#include <unistd.h>     /* for getopt() */#include <errno.h>      /* errno and perror */#include <fcntl.h>      /* O_flags */#include <stdio.h>int main(int argc, char **argv){    int perms = 0600;           /* permissions */    size_t size = 65536;        /* segment size */    int oflags = 0;             /* open flags receives -c, -x, -t */    int ropt = 0;               /* -r option seen */    int wopt = 0;               /* -w option seen */    int shm_fd;                 /* file descriptor */    int mprot = PROT_READ;      /* protection flags to mmap */    int mflags = MAP_SHARED;    /* mmap flags */    void *attach;               /* assigned memory adddress */    char *path;                 /* ->first non-option argument */    int c;    while ( -1 != (c = getopt(argc,argv,"p:s:cxrtw")) )    {        switch (c)        {        case 'p': /* permissions */            perms = (int) strtoul(optarg, NULL, 0);            break;        case 's': /* segment size */            size = (size_t) strtoul(optarg, NULL, 0);            break;        case 'c': /* use O_CREAT */            oflags |= O_CREAT;            break;        case 'x': /* use O_EXCL */            oflags |= O_EXCL;            break;        case 't': /* use O_TRUNC */            oflags |= O_TRUNC;            break;        case 'r': /* use O_RDONLY */            ropt = 1;            break;        case 'w': /* wait after attaching */            wopt = 1;            break;        default: /* unknown or missing argument */            return -1;              } /* switch */    } /* while */    if (optind < argc)        path = argv[optind]; /* first non-option argument */    else        { printf("Segment pathname required/n"); return -1; }    if (0==ropt)    { /* read-write access, reflect in mprot and mflags */        oflags |= O_RDWR;        mprot |= PROT_WRITE;        mflags |= MAP_AUTOGROW + MAP_AUTORESRV;         }    else    { /* read-only access, mprot and mflags defaults ok */        oflags |= O_RDONLY;    }    shm_fd = shm_open(path,oflags,perms);    if (-1 != shm_fd)    {        attach = mmap(NULL,size,mprot,mflags,shm_fd,(off_t)0);        if (attach != MAP_FAILED) /* mmap worked */        {            printf("Attached at 0x%lx, first word = 0x%lx/n",                                attach,      *((pid_t*)attach));            if (mprot & PROT_WRITE)            {                *((pid_t *)attach) = getpid();                printf("Set first word to 0x%lx/n",*((pid_t*)attach));            }            if (wopt) /* wait a while, report possibly-different value */            {                char inp[80];                printf("Waiting for return key before unmapping...");                gets(inp);                printf("First word is now 0x%lx/n",*((pid_t*)attach));            }            if (munmap(attach,size))                perror("munmap()");        }        else            perror("mmap()");    }    else        perror("shm_open()");    return errno;} 


IRIX Shared Memory Arenas

The shared memory arena is basic to all IRIX IPC mechanisms. IRIX semaphores, locks, and barriers are all represented as objects within a shared arena.

Overview of Shared Arenas

A shared arena is a segment of memory that can be made part of the address space of more than one process. Each shared arena is associated with a disk file that acts as a backing store for the file (see“Page Validation”). Each process that wants to share access to the arena does so by specifying the file pathname of the file. The file pathname acts as the public name of the memory segment. The file access permissions determine which user IDs and group IDs can share the file.

The functions you use to manage a shared arena are discussed in the following topics and are summarized inTable 3-2.

Table 3-2. IRIX Shared Arena Management Functions

Function Name

Purpose and Operation

usconfig(3)

Establish the default size of an arena, the number of concurrent processes that can use it, and the features of IPC objects in it.

usinit(3)

Create an arena or join an existing arena.

usadd(3)

Join an existing arena.


Initializing Arena Attributes

A program creates a shared memory arena with the usinit() function. However, many attributes of a new arena are set by preceding calls tousconfig(). The normal sequence of operations is to make several calls tousconfig() to establish arena attributes, then to make one call to usinit() to create the arena.

You call usconfig() to establish the features summarized in Table 3-3.

Table 3-3. Arena Features Set Using usconfig()

usconfig() Flag Name

Meaning

CONF_INITSIZE

The initial size of the arena segment. The default is 64 KB. Often you know that more is needed.

CONF_AUTOGROW

Whether or not the arena can grow automatically as more IPC objects or data objects are allocated (default: yes).

CONF_INITUSERS

The largest number of concurrent processes that can use the arena. The default is 8; if more processes than this will use IPC, the limit must be set higher.

CONF_CHMOD

The effective file permissions on arena access. The default is 600, allowing only processes with the effective UID of the creating process to attach the arena.

CONF_ARENATYPE

Establish whether the arena can be attached by general processes or only by members of one program (a share group).

CONF_LOCKTYPE

Whether or not lock objects allocated in the arena collect metering statistics as they are used.

CONF_ATTACHADDR

An explicit memory base address for the next arena to be created (see “Choosing a Segment Address”).

CONF_HISTON
CONF_HISTOFF

Start and stop collecting usage history (more bulky than metering information) for semaphores in a specified arena.

CONF_HISTSIZE

Set the maximum size of semaphore history records.

See the usconfig(3) reference page for a complete list of attributes. The use of metering and history information for locks and semaphores is covered inChapter 4, “Mutual Exclusion.”


Tip: In programs that use an arena and start a varying number of child processes, it is a common mistake to find that the eighth child process cannot join the arena. This occurs simply becauseusconfig() has not been called with CONF_INITUSERS to set the number of users higher than the default 8 before the arena was created.

Creating an Arena

After setting the arena attributes with usconfig(), the program callsusinit(), specifying a file pathname string.


Tip: The mktemp() library function can be used to create a unique temporary filename (see themktemp(3C) reference page).

If the specified file doesn't exist, usinit() creates it (and gives it the access permissions specified tousinit() with CONF_CHMOD). If a shared arena already exists based on that name,usinit() joins that shared arena. If the file exists but is not yet a shared arena,usinit() overwrites it. In any case, usinit() is subject to normal filesystem permission tests, and it returns an error if the process doesn't have read and write permission on the file (if it already exists) or permission to create the file (if it doesn't exist).

Code to prepare an arena is shown in Example 3-2.

Example 3-2. Initializing a Shared Memory Arena

usptr_t makeArena(size_t initSize, int nProcs){   int ret;   char * tmpname = "/var/tmp/arenaXXXXXX";   if (ret = usconfig(CONF_INITUSERS, nProcs))   { perror("usconfig(#users)"); return 0; }   if (ret = usconfig(CONF_INITSIZE, initSize))   { perror("usconfig(size)"); return 0; }   return usinit(mktemp(tmpname));}


Joining an Arena

Only one process creates a shared arena. Other processes “join” or “attach” the arena. There are three ways of doing this. When the arena is not restricted to a single process family (either by file permissions or by CONF_ARENATYPE setting), any process that calls usinit() and passes the same pathname string gains access to the same arena at the same virtual base address. This process need not be related in any way to the process that created the arena.

Restricting Access to an Arena

You can restrict arena access to a single process and the children it creates withsproc() (a share group; see “Process Creation and Share Groups”) by calling usconfig() to set CONF_ARENATYPE to US_SHAREDONLY before creating the arena. When this is done, the file is unlinked immediately after the arena is created. Then a call tousinit() with the same pathname from a different process creates a different arena, one that is not shared with the first one. This has several side-effects that are detailed inusconfig(3).

Arena Access From Processes in a Share Group

An arena is a segment in the address space of a process. When that process creates a new process usingsproc(), the child process usually shares the same address space (see thesproc(2) reference page and Chapter 12, “Process-Level Parallelism”). The child process has access to the arena segment on the same basis as the parent process. However, the child process needs to join the arena formally.

The child process should join the arena by calling usadd(), passing the address of the arena. The child should test the return code of this function, since it can reflect an error in either of two cases:

  • The arena has not been created, or an incorrect arena address was passed.

  • The arena was not configured to allow enough using processes, and no more users can be allowed.

A child process can join an arena automatically, simply by using a semaphore, lock, or barrier that was allocated within that arena. These function calls perform an automatic call tousadd(). However, they can also encounter the error that too many processes are already using the arena. It is best for the child process to check for this condition with an explicit call tousadd().

Allocating in an Arena

Allocating shared memory from a shared arena is much like the regular process of allocating memory using themalloc() and free() library routines. The functions related to allocation within an arena are summarized inTable 3-4.

Table 3-4. IRIX Shared Memory Arena Allocation Functions

Function Name

Purpose and Operation

usmalloc(3)

Allocate an object of specified size in an arena.

uscalloc(3)

Allocate an array of zero-filled units in an arena.

usmemalign(3)

Allocate an object of specified size on a specified alignment boundary in an arena.

usrealloc(3)

Change the allocated size of an object in an arena.

usrealloc(3)

Change the allocated size of an array created with uscalloc().

usmallocblksize(3)

Query the actual size of an object as allocated.

usfree(3)

Release an object allocated in an arena.

usmallopt(3)

Tune the allocation algorithm using constants described in amallopt(3).

usmallinfo(3)

Query allocation statistics (see amallinfo(3) for structure fields).

The address of an object allocated using usmalloc() or a related function is a valid address in any process that is attached to the shared arena. If the address is passed to a process that has not attached the arena, the address is not valid for that process and its use will cause a SIGSEGV.

The usmalloc() family of functions is based on the arena-allocation function family described in theamalloc(3) reference page. The usmallopt() function is the same as theamallopt() function, and both provide several options for modifying the memory allocation methods in a particular arena. In a similar way,usmallinfo() is the same as amallinfo(), and both return detailed statistics on usage of memory allocation in one arena.

Exchanging the First Datum

The processes using a shared arena typically need to locate some fundamental data structure that has been allocated within the arena. For example, the parent process creates a foundation data structure in the arena, and initializes it with pointers to other objects within the arena. Any process starting to use the arena needs the address of the foundation structure in order to find all the other objects used by the application.

The shared arena has a special one-pointer field for storing such a basic address. This area is accessed using the functions summarized inTable 3-5.

Table 3-5. IRIX Shared Memory First-Datum Functions

Function Name

Purpose and Operation

usputinfo(3)

Set the shared-pointer field of an arena to a value.

usgetinfo(3)

Retrieve the value of the shared-pointer field of an arena.

uscasinfo(3)

Change the shared-pointer field using a compare-and-swap.



Note: The precision of the usgetinfo() field in an arena, 32 or 64 bits, depends on the execution model of the program that creates the arena. This is one reason that processes compiled to different models cannot share one arena (see “Sharing Memory Between 32-Bit and 64-Bit Processes”).

Often, the parent process creates and initializes the arena before it creates any of the child processes that will share the arena. In this case, you expect no race conditions. The parent can set the shared pointer usingusputinfo() because no other process is using the arena at that time. Each child process can fetch the value withusgetinfo().

The purpose of uscasinfo() is to change the contents of the field in an atomic fashion, avoiding any race condition between concurrent processes in a multiprocessor. All three functions are discussed in detail in theusputinfo(3P) reference page.


Tip: The data type of the shared pointer field is void*, a 64-bit value when the program is compiled to the 64-bit model. If you need to cast the value to an integer, use the type__psint_t, a pointer-sized integer in any model.

In the less-common case when an arena is shared by unrelated processes, each process that callsusinit() might be the first one to create the arena—or might not. If the calling process is the first, it should initialize the basic contents and set the shared pointer. If it is not the first, it should use the initialized contents that another process has already prepared. This problem is resolved with uscasinfo(), as sketched by the code inExample 3-3.

Example 3-3. Setting Up an Arena With uscasinfo()

typedef struct arenaStuff {   ulock_t   updateLock; /* exclusive use of this structure */   short     joinedProcs; /* number of processes joined */   ...pointers to other things allocated by setUpArena()... } arenaStuff_t;/*|| The following function performs the one-time setup of the|| arenaStuff contents. It assumes that updateLock is held.*/extern voidsetUpArena(usptr_t *arena, arenaStuff_t *stuff);/*|| The following function joins a specified arena, creating it|| and initializing it if necessary.  It could be extended with|| values to pass to usconfig(3) before the arena is created.*/ usptr_t*joinArena(char *arenaPath){   usptr_t *arena;   arenaStuff_t *stuff;   int ret;   /*   || Join the arena, creating it if necessary. Exit on error.   */   if (!arena = usinit(arenaPath))   {      perror("usinit");      return arena;   }   /*   || Do the following as many times as necessary until the arena   || has been initialized.   */   for(ret=0; !ret; )   {      if (stuff = (arenaStuff_t *)usgetinfo(arena))      {         /*         || Another process has created the arena, and either has         || initialized it or is initializing it right now. Acquire         || the lock, which will block us until initializing is done.         */         ussetlock(stuff->updateLock);         /* here do anything needing exclusive use of arena */         ++stuff->joinedProcs; /* another process has joined */         usunsetlock(stuff->updateLock); /* release arena */         ret = 1; /* end the loop */      }      else      {         /*         || This process appears to be first to call usinit().         || Allocate an arenaStuff structure with its updateLock         || already held and 1 process joined, and try to swap it         || into place as the active one. We expect no errors         || in setting up arenaStuff. If one occurs, the arena is         || simply unusable, and we return a NULL to the caller.         */         if (! (stuff = usmalloc(sizeof(arenaStuff_t),arena) ) )            return stuff; /* should never occur */         if (! (stuff->updateLock = usnewlock(arena) ) );            return (usptr_t*)0; /* should never occur */         if (! uscsetlock(stuff->updateLock, 1) )            return (usptr_t*)0; /* should never occur */         stuff->joinedProcs = 1;         if (ret = uscasinfo(arena,0,stuff))         {            /*            || Our arenaStuff is now installed. Initialize it.            || We hold the lock in arenaStuff as setUpArena expects.            || The loop ends because ret is now nonzero.            */            setUpArena(arena,stuff);            usunsetlock(stuff->updateLock);         }         else         {            /*            || uscasinfo() either did not find a current value of 0            || (indicates a race with another process executing this            || code) or it failed for some other reason. In any case,            || release allocated stuff and repeat the loop (ret==0).            */            usfreelock(stuff->updatelock,arena);            usfree(stuff,arena);         }      } /* usgetinfo returned 0 */   } /* while uscasinfo swap fails */   /* arena->initialized arena, updateLock not held */   return arena;}

Example 3-3 assumes that everything allocated in the arena is accessed through a collection of pointers,arenaStuff. The two problems to be solved are these:

  • Which asynchronous process is the first to call usinit(), and therefore should allocatearenaStuff and initialize it with pointers to other objects?

  • How can the second and subsequent processes know when the initialization of arenaStuff is complete (it might take some time) and the arena is completely ready for use?

The solution in Example 3-3 is based on the discussion in the uscasinfo(3P) reference page. Each process calls functionjoinArena(). If a call to usgetinfo() returns nonzero, it is the address of anarenaStuff_t that has been allocated by some other process. Possibly that process is concurrently executing, initializing the arena. The current process waits until the lock in thearenaStuff_t is released. On return from the ussetlock() call, the process has exclusive use ofarenaStuff until it releases the lock. It uses this exclusive control to increment the count of processes using the arena.

When usgetinfo() returns 0, the calling process is probably the first to create the arena, so it allocates anarenaStuff structure, and also allocates the essential lock and puts it in a locked state. Then it callsuscasinfo() to swap the arenaStuff address for the expected value of 0. When the swap succeeds, the process completes initializing the arena and releases the lock.

The call to uscasinfo() could fail if, between the time the process receives a 0 fromusgetinfo() and the time it calls uscasinfo(), another process executes this same code and installs its ownarenaStuff. The process handles this unusual event by releasing the items it allocated and repeating the whole process.

When unrelated processes join an arena with code like that shown in Example 3-3, they should terminate their use of the arena with code similar toExample 3-4.

Example 3-4. Resigning From an Arena

/*|| The following function reverses the operation of joinArena.|| Even if the calling process is the last one to hold the arena, || nothing drastic is done. This is because it is impossible to|| perform {usinit(); usgetinfo(); ussetlock();} as an atomic|| sequence.  Once an arena comes into being it must remain|| usable until the entire application shuts down. Unlinking the|| arena file can be the last thing that main() does.*/voidresignArena(usptr_t *arena){   arenaStuff_t *stuff = (arenaStuff_t *)usgetinfo(arena);   ussetlock(stuff->updateLock);   -- stuff->joinedProcs;   usunsetlock(stuff->updateLock);}

It might seem that, when the function resignArena() in Example 3-4 finds that it has reduced the joinedProcs count to 0, it could deinitialize the arena, for example unlinking the file on which the arena is based. This is not a good idea because of the remote chance of the following sequence of events:

  1. Process A executes joinArena(), initializing the arena.

  2. Unrelated process B executes joinArena() through the usinit() call, but is suspended for a higher-priority process before executingusgetinfo().

  3. Process A detects some error unrelated to arena use, and as part of termination, callsresignArena().

  4. Process B resumes execution with the call to usgetinfo().

If the resignArena() function did something irrevocable, such as unlinking or truncating the arena file, it would leave process B in an unexpected state.

System V Shared Memory Functions

The System V shared memory functions allow two or more processes to share memory. Unlike the IRIX method, in which the external name of a shared arena is also the name of a file, the external name of an SVR4 shared segment is an integer held in an IPC name table (see “SVR4 IPC Name Space”).

The functions and commands used with SVR4 shared memory are discussed in the following topics and summarized inTable 3-6.

Table 3-6. SVR4 Shared Memory Functions

Function Name

Purpose and Operation

shmget(2)

Create a shared memory IPC object or return the ID of one.

shmctl(2)

Get the status of a shared memory segment, change permissions or user IDs, or lock or unlock a segment in memory.

shmat(2)

Attach a shared memory segment to the address space.

shmdt(2)

Detach a shared memory segment from the address space.


Creating or Finding a Shared Memory Segment

A process creates a shared memory segment, or locates an existing segment, using theshmget() system function. When it creates a segment, the arguments to this function establish:

  • The numeric key of the segment.

  • The size of the segment.

  • The user ID and group ID of the segment creator and owner.

  • The access permissions to the segment.

When the function locates an existing segment, access to the segment is controlled by the access permissions and by the user ID and group ID of the calling process.

Unlike an IRIX shared arena, a shared segment does not grow automatically as it is used. The size specified when it is created is fixed. The shared segment is initialized to binary zero. (As implemented in IRIX, the pages of the segment are created as they are first referenced; see “Mapping a Segment of Zeros”.)

The value returned by shmget() is the ID number of the segment. It is used to identify the segment to other functions.

Attaching a Shared Segment

The shmget() function creates the segment, or verifies that it exists, but does not actually make it a part of the process address space. That remains to be done with a call toshmat() (“attach”), passing the identifier returned by shmget().

You can pass a desired base address to shmat(), or you can pass NULL to have the system select the base address. It is best to let the system choose the base; this ensures that all processes have the same base address for the segment.

A process can detach a segment from its address space by calling shmdt().

Managing a Shared Segment

The shmctl() function gives you the ability to get information about a segment, or to modify its attributes. These operations are summarized inTable 3-7.

Table 3-7. SVR4 Shared Segment Management Operations

Keyword

Operation

Can Be Used By

IPC_STAT

Get information about the segment.

Any process having read access.

IPC_SET

Set owner UID, owner GID, or access permissions.

Creator UID, owner UID, or superuser.

IPC_RMID

Remove the segment from the IPC name space.

Creator UID, owner UID, or superuser.

SHM_LOCK

Lock the segment pages in memory.

Superuser process only.

SHM_UNLOCK

Unlock a locked segment.

Superuser process only.


Information About Shared Memory

The information structure returned by shmctl(IPC_STAT) is declared in thesys/shm.h header file. The first field, shm_perm, is an ipc_perm structure. This structure is declared in thesys/ipc.h header file.

Shared Memory Examples

The example programs in this section illustrate the use of some of the SVR4 shared memory system functions.

Example of Creating a Shared Segment

The program in Example 3-5 illustrates the use of shmget(). You can specify command parameters to exercise any combination ofshmget() function arguments.

Example 3-5. shmget() System Call Example

/*||  Program to test shmget(2) for creating a segment.||  shmget [-k <key>] [-s <size>] [-p <perms>] [-c] [-x]||      -k <key>            the key to use, default == 0 == IPC_PRIVATE||      -s <size>       size of segment, default is 64KB||      -p <perms>      permissions to use, default is 0600||      -x                  use IPC_EXCL||      -c                  use IPC_CREAT*/#include <unistd.h> /* for getopt() */#include <sys/shm.h>    /* for shmget etc */#include <errno.h>  /* errno and perror */#include <stdio.h>int main(int argc, char **argv){    key_t key = IPC_PRIVATE;    /* key */    size_t size = 65536;            /* size */    int perms = 0600;               /* permissions */    int shmflg = 0;             /* flag values */    struct shmid_ds ds;         /* info struct */    int c, shmid;    while ( -1 != (c = getopt(argc,argv,"k:s:p:cx")) )    {        switch (c)        {        case 'k': /* key */            key = (key_t) strtoul(optarg, NULL, 0);            break;        case 's': /* size */            size = (size_t) strtoul(optarg, NULL, 0);            break;        case 'p': /* permissions */            perms = (int) strtoul(optarg, NULL, 0);            break;        case 'c':            shmflg |= IPC_CREAT;            break;        case 'x':            shmflg |= IPC_EXCL;            break;        default: /* unknown or missing argument */            return -1;              }    }    shmid = shmget(key,size,shmflg|perms);    if (-1 != shmid)    {        printf("shmid = %d (0x%x)/n",shmid,shmid);        if (-1 != shmctl(shmid,IPC_STAT,&ds))        {            printf("owner uid/gid: %d/%d/n",                            ds.shm_perm.uid,ds.shm_perm.gid);            printf("creator uid/gid: %d/%d/n",                            ds.shm_perm.cuid,ds.shm_perm.cgid);        }        else            perror("shmctl(IPC_STAT)");    }    else        perror("shmget");    return errno;} 


Example of Attaching a Shared Segment

The program in Example 3-6 illustrates the process of actually attaching to and using a shared memory segment. The segment must exist, and is specified by its ID or by its key. You can use the program inExample 3-5 to create a segment for this program to use.

The attachment is either read-write or read-only, depending on the presence of the-r command parameter. When the program attaches the segment read-write, it stores its own PID in the first word of the segment. Run the program several times; each time it reports the previous PID value and sets a new PID value. This illustrates that the contents of the segment persist between uses of the segment.

You can use the -w parameter to have the program wait after attaching. This allows you to start more copies of the program so that multiple processes have attached the segment.

Example 3-6. shmat() System Call Example

/*||  Program to test shmat().||      shmat {-k <key> | -i <id>} [-a <addr>] [-r] [-w]||          -k <key>        the key to use to get an ID..||          -i <id>     ..or the ID to use||          -a <addr>   address to attach, default=0||          -r              attach read-only, default read/write||          -w              wait on keyboard input before detaching*/#include <unistd.h> /* for getopt() */#include <sys/shm.h> /* for shmget etc */#include <errno.h> /* errno and perror */#include <stdio.h>int main(int argc, char **argv){    key_t key = -1; /* key */    int shmid = -1; /* ..or ID */    void *addr = 0; /* address to request */    void *attach;       /* address gotten */    int rwflag = 0; /* read or r/w */    int wait = 0;       /* wait before detach */    int c, ret;    while ( -1 != (c = getopt(argc,argv,"k:i:a:rw")) )    {        switch (c)        {        case 'k': /* key */            key = (key_t) strtoul(optarg, NULL, 0);            break;        case 'i': /* id */            shmid = (int) strtoul(optarg, NULL, 0);            break;        case 'a': /* addr */            addr = (void *) strtoul(optarg, NULL, 0);            break;        case 'r': /* read/write */            rwflag = SHM_RDONLY;            break;        case 'w': /* wait */            wait = 1;            break;        default:            return -1;        }    }    if (-1 == shmid) /* key must be given */        shmid = shmget(key,0,0);    if (-1 != shmid) /* we have an ID */    {        attach = shmat(shmid,addr,rwflag);        if (attach != (void*)-1)        {            printf("Attached at 0x%lx, first word = 0x%lx/n",                                attach,      *((pid_t*)attach));            if (rwflag != SHM_RDONLY)            {                *((pid_t *)attach) = getpid();                printf("Set first word to 0x%lx/n",*((pid_t*)attach));            }            if (wait)            {                char inp[80];                printf("Press return to detach...");                gets(inp);                printf("First word is now 0x%lx/n",*((pid_t*)attach));            }            if (shmdt(attach))                perror("shmdt()");        }        else            perror("shmat()");    }    else        perror("shmget()");    return errno;} 
原创粉丝点击