Quick Overview of Shared Memory


For the impatient reader, we ll now take a quick look at the shared memory APIs. In the next section, we ll dig into the API further. Here we ll look at code snippets to create a shared memory segment, get an identifier for an existing one, configure a segment, attach and detach, and also some examples of processes using them.

Using the shared memory API requires the function prototypes and symbols to be available to the application. This is done by including (at the top of the C source file):

 #include <sys/ipc.h>     #include <sys/shm.h> 

Create a Shared Memory Segment

To create a shared memory segment, we use the shmget API function. Using shmget , we specify a unique shared memory ID, the size of the segment, and finally a set of flags (see Listing 17.1). The flags argument, as we saw with message queues and semaphores, includes both access permissions and a command to create the segment ( IPC_CREAT ).

Listing 17.1: Creating a Shared Memory Segment with shmget (on the CD-ROM at ./source/ch17/shmcreate.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include "common.h"  4  :  5  :       int main()  6  :       {  7  :         int shmid;  8  :  9  :         /* Create the shared memory segment using MY_SHM_ID */  10  :         shmid =  shmget  (MY_SHM_ID, 4096, 0666  IPC_CREAT);  11  :  12  :         if (shmid >= 0) {  13  :  14  :           printf("Created a shared memory segment %d\n", shmid);  15  :  16  :         }  17  :  18  :         return 0;  19  :       } 
end example
 

At line 10 in Listing 17.1, we create a new shared memory segment that s 4KB in size. The size specified must be evenly divisible by the page size of the architecture in question (typically 4KB). The return value of shmget (stored in shmid ) can be used in subsequent calls to configure or attach to the segment.

Note  

To identify the page size on a given system, simply call the getpagesize function. This returns the number of bytes contained within a system page.

 #include <unistd.h>     int  getpagesize  (void); 

Getting Information on a Shared Memory Segment

We can also get information about a shared memory segment and even set some parameters. The shmctl API function provides a number of capabilities. Here, we ll look at retrieving information about a shared memory segment.

In Listing 17.2, we first get the shared memory identifier for the segment using the shmget API function. Once we have this, we can call shmctl to grab the current stats. To shmctl we pass the identifier for the shared memory segment, the command to grab stats ( IPC_STAT ), and finally a buffer in which the data will be written.

This buffer is a structure of type shmid_ds . We ll look more at this structure in the shmctl section, later in this chapter. Upon successful return of shmctl , identified by a zero return, we emit our data of interest. Here, we emit the size of the shared memory segment ( shm_segsz ) and the number of attaches that have been performed on the segment ( shm_nattch ).

Listing 17.2: Retrieving Information about a Shared Memory Segment (on the CD-ROM at ./source/ch17/shmszget.c)
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <errno.h>  4  :       #include "common.h"  5  :  6  :       int main()  7  :       {  8  :         int shmid, ret;  9  :         struct shmid_ds shmds;  10  :  11  :         /* Get the shared memory segment using MY_SHM_ID */  12  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  13  :  14  :         if (shmid >= 0) {  15  :  16  :           ret =  shmctl  (shmid, IPC_STAT, &shmds);  17  :  18  :           if (ret == 0) {  19  :  20  :             printf("Size of memory segment is %d\n", shmds.shm_segsz);  21  :             printf("Number of attaches %d\n", (int)shmds.shm_nattch);  22  :  23  :           } else {  24  :  25  :             printf("shmctl failed (%d)\n", errno);  26  :  27  :           }  28  :  29  :         } else {  30  :  31  :           printf("Shared memory segment not found.\n");  32  :  33  :         }  34  :  35  :         return 0;  36  :       } 
end example
 

Attaching and Detaching a Shared Memory Segment

In order to use our shared memory, we must attach to it. Attaching to a shared memory segment maps the shared memory into our process s memory space. To attach to the segment, we use the shmat API function. This returns a pointer to the segment in the process s address space. This address can then be used by the process like any other memory reference. We detach from the memory segment using the shmdt API function.

In Listing 17.3, a simple application is shown to attach to and detach from a shared memory segment. We first get the shared memory identifier using shmget (at line 12). At line 16, we attach to the segment using shmat . We specify our identifier and an address (where we d like to place it in our address space) and an options word (0). After checking, if this was successful (the return of a nonzero address from shmat ), we detach from the segment at line 23 using shmdt .

Listing 17.3: Attaching to and Detaching from a Shared Memory Segment (on the CD-ROM at ./source/ch17/shmattch.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <errno.h>  4  :       #include "common.h"  5  :  6  :       int main()  7  :       {  8  :         int shmid, ret;  9  :         void *mem;  10  :  11  :         /* Get the shared memory segment using MY_SHM_ID */  12  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  13  :  14  :         if (shmid >= 0) {  15  :  16  :           mem =  shmat  (shmid, (const void *)0, 0);  17  :  18  :           if ((int)mem != -1) {  19  :  20  :             printf("Shared memory was attached in our "  21  :                     "address space at %p\n", mem);  22  :  23  :             ret =  shmdt  (mem);  24  :  25  :             if (ret == 0) {  26  :  27  :               printf("Successfully detached memory\n");  28  :  29  :             } else {  30  :  31  :               printf("Memory detached Failed (%d)\n", errno);  32  :  33  :             }  34  :  35  :           } else {  36  :  37  :             printf("shmat failed (%d)\n", errno);  38  :  39  :           }  40  :  41  :         } else {  42  :  43  :           printf("Shared memory segment not found.\n");  44  :  45  :         }  46  :  47  :         return 0;  48  :       } 
end example
 

Using a Shared Memory Segment

Now let s look at two processes that use a shared memory segment. For brevity, we ll pass on the error checking for this example. We ll first look at the write example. In Listing 17.4, we see a short example that uses the strcpy standard library function to write to the shared memory segment. Since the segment is just a block of memory, we cast it from a void pointer to a character pointer in order to write to it (avoiding compiler warnings) at line 16. It s important to note that a shared memory segment is nothing more than a block of memory, and anything you would expect to do with a memory reference is possible with the shared memory block.

Listing 17.4: Writing to a Shared Memory Segment (on the CD-ROM at ./source/ch17/_shmwrite.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <string.h>  4  :       #include "common.h"  5  :  6  :       int main()  7  :       {  8  :         int shmid, ret;  9  :         void *mem;  10  :  11  :         /* Get the shared memory segment using MY_SHM_ID */  12  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  13  :  14  :         mem =  shmat  (shmid, (const void *)0, 0);  15  :  16  :         strcpy((char *)mem, "This is a test string.\n");  17  :  18  :         ret =  shmdt  (mem);  19  :  20  :         return 0;  21  :       } 
end example
 

Now let s look at a read example. In Listing 17.5, we see a similar application to Listing 17.4. In this particular case, we read from the block of memory by using the printf call. In Listing 17.4 (the write application), we copied a string into the block with the strcpy function. Now in Listing 17.5, we emit that same string using printf . Note that the first process attaches to the memory, writes the string, and then detaches and exits. The next process attaches and reads from the memory. Any number of processes could read or write to this memory, which is one of the basic problems. We ll investigate some solutions in the section Using a Shared Memory Segment, later in this chapter.

Listing 17.5: Reading from a Shared Memory Segment (on the CD-ROM at ./source/_ch17/shmread.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <string.h>  4  :       #include "common.h"  5  :  6  :       int main()  7  :       {  8  :         int shmid, ret;  9  :         void *mem;  10  :  11  :         /* Get the shared memory segment using MY_SHM_ID */  12  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  13  :  14  :         mem =  shmat  (shmid, (const void *)0, 0);  15  :  16  :         printf("%s", (char *)mem);  17  :  18  :         ret =  shmdt  (mem);  19  :  20  :         return 0;  21  :       } 
end example
 

Removing a Shared Memory Segment

To permanently remove a shared memory segment, we use the shmctl API function. We use a special command with shmctl called IPC_RMID to remove the segment (much as is done with message queues and semaphores). Listing 17.6 illustrates the segment removal.

Listing 17.6: Removing a Shared Memory Segment (on the CD-ROM at ./source/_ch17/shmdel.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <errno.h>  4  :       #include "common.h"  5  :  6  :       int main()  7  :       {  8  :         int shmid, ret;  9  :  10  :         /* Create the shared memory segment using MY_SHM_ID */  11  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  12  :  13  :         if (shmid >= 0) {  14  :  15  :           ret =  shmctl  (shmid, IPC_RMID, 0);  16  :  17  :           if (ret == 0) {  18  :  19  :             printf("Shared memory segment removed\n");  20  :  21  :           } else {  22  :  23  :             printf("shmctl failed (%d)\n", errno);  24  :  25  :           }  26  :  27  :         } else {  28  :  29  :           printf("Shared memory segment not found.\n");  30  :  31  :         }  32  :  33  :         return 0;  34  :       } 
end example
 

Once the shared memory segment identifier is found (line 11), we call shmctl with the IPC_RMID argument at line 15.

That completes our quick tour of the shared memory API. In the next section we ll dig deeper into the APIs and look at some of the more detailed aspects.

Shared Memory APIs

Now that we have a quick review behind us, let s dig down further into the APIs. Table 17.1 provides the shared memory API functions, along with their basic uses.

Table 17.1: Shared Memory API Functions and Uses

API Function

Uses

shmget

Create a new shared memory segment

 

Get the identifier for an existing shared memory segment

shmctl

Get info on a shared memory segment

 

Set certain info on a shared memory segment

 

Remove a shared memory segment

shmat

Attach to a shared memory segment

shmdt

Detach from a shared memory segment

We ll address these API functions now in detail, identifying each of their uses with example source.

shmget

The shmget API function (like semget and msqget ) is a multirole function. First, it can be used to create a new shared memory segment, and second, it can be used to get the ID of an existing shared memory segment. The result of the shmget API function (in either role) is a shared memory segment identifier that is to be used in all other shared memory functions. The prototype for the shmget function is defined as:

 #include <sys/ipc.h>     #include <sys/shm.h>     int  shmget  (key_t key, size_t size, int shmflag); 

The key argument specifies a system-wide identifier that uniquely identifies the shared memory segment. The key must be a nonzero value or the special symbol IPC_PRIVATE . The IPC_PRIVATE argument defines that we re creating a private segment (one that has no system-wide name ). No other processes will find this segment, but it can be useful to create segments that are used only within a process or process group (where the return key can be communicated).

The size argument identifies the size of the shared memory segment to create. When we re interested in an existing shared memory segment, we leave this argument as zero (as it s not used by the function in the create case). As a minimum, the size must be PAGE_SIZE (or 4Kb). The size should also be evenly divisible by PAGE_SIZE , as the segment allocated will be in PAGE_SIZE chunks . The maximum size is implementation dependent, but typically is 4MB.

The shmflag argument permits the specification of two separate parameters. These are a command and an optional set of access permissions. The command portion can take one of three forms. The first is to create a new shared memory segment, where the shmflag is equal to the IPC_CREAT symbol. This returns the identifier for a new segment or an identifier for an existing segment (if it already exists). If we want to create the segment and fail if the segment already exists, then we can use the IPC_CREAT with the IPC_EXCL symbol (second form). If ( IPC_CREAT IPC_EXCL ) is used and the shared segment already exists, then an error status is returned and errno is set to EEXIST . The final form simply requests an existing shared memory segment. In this case, we specify a value of zero for the command argument.

When creating a new shared memory segment, each of the read/write access permissions can be used except for the execute permissions. These permissions are shown in Table 17.2.

Table 17.2: Shared Memory Segment Permissions for shmget msgflag Argument

Symbol

Value

Meaning

S_IRUSR

0400

User read permission

S_IWUSR

0200

User write permission

S_IRGRP

0040

Group read permission

S_IWGRP

0020

Group read permission

S_IROTH

0004

Other read permission

S_IWOTH

0002

Other read permission

Let s now look at a few examples of the shmget function to create new shared memory segments or to access existing ones.

In this first example, we ll create a new private shared memory segment of size 4KB. Note that since we re using IPC_PRIVATE , we re assured of creating a new segment as no unique key is provided. We ll also specify full read and write permission to all (system, group, and user).

 shmid =  shmget  (IPC_PRIVATE, 4096, IPC_CREAT  0666); 

If the shmget API function fails, a “1 is returned (as shmid ) with the actual error specified in the special errno variable (for this particular process).

Now let s look at the creation of a memory segment, with an error return if the segment already exists. In this example, our system-wide identifier ( key ) is 0x123 , and we ll request a 64KB segment.

 shmid =  shmget  (0x123, (64 * 1024), (IPC_CREAT  IPC_EXCL  0666));     if (shmid == -1) {       printf("shmget failed (%d)\n", errno);     } 

Here we use the IPC_CREAT with IPC_EXCL to ensure that the segment doesn t exist. If we get a -1 return from shmget , an error occurred (such as the segment already existed).

Creating system-wide keys with ftok was discussed in Chapter 15, IPC with Message Queues, and Chapter 16, Synchronization with Semaphores. Please refer to those chapters for a detailed discussion of file-based key creation.

Finally, let s look at a simple example of finding the shared memory identifier for an existing segment.

 shmid =  shmget  (0x123, 0, 0);     if (shmid != -1) {       // Found our shared memory segment     } 

Here we specify only the system-wide key; segment size and flags are both zero (as we re getting the segment, not creating it).

A final point to discuss with shared memory segments creation is the initialization of the shared memory data structure that is contained within the kernel. The shared memory structure is shown in Listing 17.7.

Listing 17.7: The Shared Memory Structure ( shmid_ds )
start example
 struct shmid_ds {   struct ipc_perm shm_perm    /* Access permissions         */   int    shm_segsz;        /* Segment size (in bytes)    */   time_t shm_atime;        /* Last attach time (shmat)   */   time_t shm_dtime;        /* Last detach time (shmdt)   */   time_t shm_ctime;        /* Last change time (shmctl)  */   unsigned short shm_cpid;    /* Pid of segment creator     */   unsigned short shm_lpid;    /* Pid of last segment user   */   short  shm_nattch;        /* Number of current attaches */ }; struct ipc_perm {   key_t __key;   unsigned short uid;   unsigned short gid;   unsigned short cuid;   unsigned short cgid;   unsigned short mode;   unsigned short pad1;   unsigned short __seq;   unsigned short pad2;   unsigned long int __unused1;   unsigned long int __unused2; }; 
end example
 

Upon creation of a new shared memory segment, the shm_perm structure is initialized with the key and creator s user ID and group ID. Other initializations are shown in Table 17.3.

Table 17.3: Shared Memory Data Structure init on Creation

Field

Initialization

shm_segsz

Segment size provided to shmget

shm_atime

shm_dtime

shm_ctime

Current time

shm_cpid

Calling process s PID

shm_lpid

shm_nattch

shm_perm.cuid

Creator s process user ID

shm_perm.gid

Creator s process group ID

We ll return to these elements shortly when we discuss the control aspects of shared memory segments.

shmctl

The shmctl API function provides three separate functions. The first is to read the current shared memory structure (as defined at Listing 17.7) using the IPC_STAT command. The second is to write the shared memory structure using the IPC_SET command. Finally, a shared memory segment can be removed using the IPC_RMID command. The shmctl function prototype is shown below:

 #include <sys/ipc.h>     #include <sys/shm.h>     int  shmctl  (int shmid, int cmd, struct shmid_ds *buf); 

Let s begin by removing a shared memory segment. To remove a segment, we first must have the shared memory identifier. We then pass this identifier, along with the command IPC_RMID , to shmctl , as:

 int shmid, ret;     ...     shmid =  shmget  (SHM_KEY, 0, 0);     if (shmid != -1) {       ret =  shmctl  (shmid, IPC_RMID, 0);       if (ret == 0) {         // shared memory segment successfully removed.       }     } 

If no processes are currently attached to the shared memory segment, then the segment is removed. If there are currently attaches to the shared memory segment, then the segment is marked for deletion but not yet deleted. This means that only after the last process detaches from the segment will it be removed. Once the segment is marked for deletion, no processes may attach to the segment. Any attempt to attach results in an error return with errno set to EIDRM .

Note  

Internally, once the shared memory segment is removed, its key is changed to IPC_PRIVATE . This disallows any new process from finding the segment.

Next, let s look at the IPC_STAT command that can be used within shmctl to gather information about a shared memory segment. In Listing 17.7, we listed a number of parameters that define a shared memory segment. This structure can be read via the IPC_STAT command as shown in Listing 17.8.

Listing 17.8: Shared Memory Data Structure Elements Accessible Through shmctl (on the CD-ROM at ./source/ch17/shmstat.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <errno.h>  4  :       #include <time.h>  5  :       #include "common.h"  6  :  7  :       int main()  8  :       {  9  :         int shmid, ret;  10  :         struct shmid_ds shmds;  11  :  12  :         /* Create the shared memory segment using MY_SHM_ID */  13  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  14  :  15  :         if (shmid >= 0) {  16  :  17  :           ret =  shmctl  (shmid, IPC_STAT, &shmds);  18  :  19  :           if (ret == 0) {  20  :  21  :             printf("Size of memory segment is %d\n",  22  :                       shmds.shm_segsz);  23  :             printf("Number of attaches %d\n",  24  :                       (int)shmds.shm_nattch);  25  :             printf("Create time %s",  26  :                       ctime(&shmds.shm_ctime));  27  :             if (shmds.shm_atime) {  28  :               printf("Last attach time %s",  29  :                         ctime(&shmds.shm_atime));  30  :             }  31  :             if (shmds.shm_dtime) {  32  :               printf("Last detach time %s",  33  :                         ctime(&shmds.shm_dtime));  34  :             }  35  :             printf("Segment creation user %d\n",  36  :                       shmds.shm_cpid);  37  :             if (shmds.shm_lpid) {  38  :               printf("Last segment user %d\n",  39  :                        shmds.shm_lpid);  40  :             }  41  :             printf("Access permissions 0%o\n",  42  :                       shmds.shm_perm.mode);  43  :  44  :           } else {  45  :  46  :             printf("shmctl failed (%d)\n", errno);  47  :  48  :           }  49  :  50  :         } else {  51  :  52  :           printf("Shared memory segment not found.\n");  53  :  54  :         }  55  :  56  :         return 0;  57  :       } 
end example
 

Listing 17.8 is rather self-explanatory. After getting our shared memory identifier at line 12, we use shmctl to grab the shared memory structure at line 17. Upon success of shmctl , we emit the various accessible data elements using printf . Note that at lines 27 and 31, we check that the time values are nonzero. If there have been no attaches or detaches, the value will be zero, and therefore there s no reason to convert it to a string time.

The final command available with shmctl is IPC_SET . This permits the caller to update certain elements of the shared memory segment data structure. These elements are shown in Table 17.4.

The following code snippet illustrates setting new permission (see Listing 17.9). It s important that the shared memory data structure be read first to get the current set of parameters.

Table 17.4 :  Shared Memory Data Structure Writeable Elements

Field

Description

shm_perm.uid

Owner Process effective user ID

shm_perm.gid

Owner Process effective group ID

shm_flags

Access permissions

shm_ctime

Takes the current time of shmctl.IPC_SET action

isting 17.9: Changing Access Permissions in a Shared Memory Segment (on the CD-ROM at ./source/ch17/shmset.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <errno.h>  4  :       #include <time.h>  5  :       #include "common.h"  6  :  7  :       int main()  8  :       {  9  :         int shmid, ret;  10  :         struct shmid_ds shmds;  11  :  12  :         /* Create the shared memory segment using MY_SHM_ID */  13  :         shmid =  shmget  (MY_SHM_ID, 0, 0);  14  :  15  :         if (shmid >= 0) {  16  :  17  :           ret =  shmctl  (shmid, IPC_STAT, &shmds);  18  :  19  :           if (ret == 0) {  20  :  21  :             printf("old permissions were 0%o\n", shmds.shm_perm.mode);  22  :  23  :             shmds.shm_perm.mode = 0444;  24  :  25  :             ret =  shmctl  (shmid, IPC_SET, &shmds);  26  :  27  :             ret =  shmctl  (shmid, IPC_STAT, &shmds);  28  :  29  :             printf("new permissions are 0%o\n", shmds.shm_perm.mode);  30  :  31  :           } else {  32  :  33  :             printf("shmctl failed (%d)\n", errno);  34  :  35  :           }  36  :  37  :         } else {  38  :  39  :           printf("Shared memory segment not found.\n");  40  :  41  :         }  42  :  43  :         return 0;  44  :       } 
end example
 

In Listing 17.9, we grab the current data structure for the memory segment at line 17 and then change the mode at line 23. We write this back to the segment s data structure at line 25 using the IPC_SET command, and then we read it back out at line 27. Not very exciting, but the key to remember is to read the structure first. Otherwise, the effective user and group IDs will be incorrect, leading to anomalous behavior.

One final topic for shared memory control that differs from message queues and semaphores is the ability to lock down segments so that they re not candidates for swapping out of memory. This can be a performance benefit, because rather than the segment being swapped out to the file system, it stays in memory and is therefore available to applications without having to swap it back in. This mechanism is therefore very useful from a performance standpoint. The shmctl API function provides the means both to lock down a segment and also to unlock.

The following examples illustrate the lock and unlock of a shared memory segment:

 int shmid;     ...     shmid =  shmget  (MY_SHM_ID, 0, 0);     ret =  shmctl  (shmid, SHM_LOCK, 0);     if (ret == 0) {       printf("Shared Memory Segment Locked down.\n");     } 

Unlocking the segment is very similar. Rather than specify SHM_LOCK , we instead use the SHM_UNLOCK symbolic, as:

 ret =  shmctl  (shmid, SHM_UNLOCK, 0); 

As before, a zero return indicates success of the shmctl call. Only a super-user may perform this particular command via shmctl .

shmat

Once the shared memory segment has been created, a process must attach to it to make it available within its address space. This is provided by the shmat API function. Its prototype is defined as:

 #include <sys/types.h>     #include <sys/shm.h>     void *  shmat  (int shmid, const void *shmaddr, int shmflag); 

The shmat function takes the shared memory segment identifier (returned by shmget ), an address where the process would like to insert this segment in the process s address space (a desired address), and a set of flags. The desired address ( shmaddr ) is rounded down if the SHM_RND flag is set within shmflags . This option is rarely used because the process would need to have explicit knowledge of the available address regions within the process s address space. This method also is not entirely portable. To have the shmat API function automatically place the region within the process s address space, a (const void *)NULL argument is passed.

The caller can also specify the SHM_READONLY within shmflags to enforce a read-only policy on the segment for this particular process. This process must first have read permission on the segment. If SHM_READONLY is not specified, it is assumed that the segment is being mapped for both read and write. There is no write-only flag.

The return value from shmat is the address in which the shared memory segment is mapped into this process. A quick example of shmat is shown here:

 int shmid;     void *myAddr;     /* Get the id for an existing shared memory segment */     shmid =  shmget  (MY_SHM_SEGMENT, 0, 0);     /* Map the segment into our space */     myAddr =  shmat  (shmid, 0, 0);     if ((int)myAddr != -1) {       // Attach failed.     } else {       // Attach succeeded.     } 

Upon completion, myAddr will contain an address in which the segment is attached or -1 , indicating that the segment failed to be attached. The return address can then be utilized by the process just like any other address.

Note  

The local address into which the shared memory segment is mapped may be different for every process that attaches to it. Therefore, no process should assume that since another mapped at a given address, it will be available to it at the same local address.

Upon successful completion of the shmat call, the shared memory data structure is updated as follows . The shm_atime field is updated with the current time (last attach time), shm_lpid is updated with the effective process ID for the calling process, and the shm_nattch field is incremented by 1 (the number of processes currently attached to the segment).

When a processes exits, its shared memory segments are automatically detached. Despite this, developers should detach from their segments using shmdt rather than relying on GNU/Linux to do this for them. Also, when a process forks into a parent and child, the child inherits any shared memory segments that were created previously by the parent.

shmdt

The shmdt API function detaches an attached shared memory segment from a process. When a process no longer needs access to the memory, this function frees it and also unmaps the memory mapped into the process s local address space that was occupied by this segment. The function prototype for the shmdt function is:

 #include <sys/types.h>     #include <sys/shm.h>     int  shmdt  (const void *shmaddr); 

The caller provides the address that was provided by shmat (as its return value). A return value of zero indicates a successful detach of the segment. Consider the following code snippet as a demonstration of the shmdt call:

 int shmid;     void *myAddr;     /* Get the id for an existing shared memory segment */     shmid =  shmget  (MY_SHM_SEGMENT, 0, 0);     /* Map the segment into our space */     myAddr =  shmat  (shmid, 0, 0);     ...     /* Detach (unmap) the segment */     ret =  shmdt  (myAddr);     if (ret == 0) {       /* Segment detached */     } 

Upon successful detach, the shared memory structure is updated as follows. The shm_dtime field is updated with the current time (of the shmdt call), the shm_lpid is updated with the process ID of the process calling shmdt , and finally the shm_nattach field is decremented by 1.

The address region mapped by the shared memory segment will be unavailable to the process and may result in a segment violation if an access is attempted.

If the segment had been previously marked for deletion (via a prior call to _ shmctl with the command of IPC_RMID ) and the number of current attaches is zero, then the segment is removed.

Shared memory can be a powerful mechanism for communication and coordination between processes. With this power comes some complexity. Since shared memory is a resource that s available to all processes that attach to it, we must coordinate access to it. One mechanism is to simply add a semaphore to the shared memory segment. If the segment represents multiple contexts, multiple semaphores can be created, each coordinating its respective access to a portion of the segment.

Let s look at a simple example of coordinating access to a shared memory segment. Listing 17.10 illustrates a simple application that provides for creating, using, and removing a shared memory segment. As we ve already covered the creation and removal aspects in detail (lines 31 “58 for create and lines 137 “158 for remove), the use scenario (lines 59 “111) is what we ll focus on here.

Our block (which represents our shared memory block) is typdef d at lines 11 “15. This contains our shared structure ( string ), a counter (as the index to our string), and our semaphore to coordinate access. Note that this was loaded into our shared structure at line 48.

Our use scenario begins by grabbing the user character passed as the second argument from the command line. This is the character we ll place into the buffer on each pass. We ll invoke this process twice with different characters to see each access the shared structure in a synchronized way. After getting the shared memory key (via shmget at line 69), we attach to the segment at line 72. The return value is the address of our shared block, which we cast to our block type ( MY_BLOCK_TYPE ). We then loop through a count of 2500, each iteration acquiring the semaphore, loading our character into the string array of the shared memory segment (our critical section), and then releasing the semaphore.

isting 17.10: Shared Memory Example Using Semaphore Coordination (on the CD-ROM at ./source/ch17/shmexpl.c )
start example
  1  :       #include <stdio.h>  2  :       #include <sys/shm.h>  3  :       #include <sys/sem.h>  4  :       #include <string.h>  5  :       #include <stdlib.h>  6  :       #include <unistd.h>  7  :       #include "common.h"  8  :  9  :       #define MAX_STRING      5000  10  :  11  :       typedef struct {  12  :         int semID;  13  :         int counter;  14  :         char string[MAX_STRING+  1  ];  15  :       } MY_BLOCK_T;  16  :  17  :  18  :       int main(int argc, char *argv[])  19  :       {  20  :         int shmid, ret, i;  21  :         MY_BLOCK_T *block;  22  :         struct sembuf sb;  23  :         char user;  24  :  25  :         /* Make sure theres a command */  26  :         if (argc >= 2) {  27  :  28  :           /* Create the shared memory segment and init it  29  :            * with the semaphore  30  :            */  31  :           if (!strncmp(argv[1], "create", 6)) {  32  :  33  :             /* Create the shared memory segment and semaphore */  34  :  35  :             printf("Creating the shared memory segment\n");  36  :  37  :             /* Create the shared memory segment */  38  :             shmid =  shmget  (MY_SHM_ID,  39  :                              sizeof(MY_BLOCK_T), (IPC_CREAT  0666));  40  :  41  :             /* Attach to the segment */  42  :             block = (MY_BLOCK_T *)  shmat  (shmid, (const void *)0, 0);  43  :  44  :             /* Initialize our write pointer */  45  :             block->counter = 0;  46  :  47  :             /* Create the semaphore */  48  :             block->semID =  semget  (MY_SEM_ID, 1, (IPC_CREAT  0666));  49  :  50  :             /* Increment the semaphore */  51  :             sb.sem_num = 0;  52  :             sb.sem_op = 1;  53  :             sb.sem_flg = 0;  54  :             semop(block->semID, &sb, 1);  55  :  56  :             /* Now, detach from the segment */  57  :  shmdt  ((void *)block);  58  :  59  :           } else if (!strncmp(argv[  1  ], "use", 3)) {  60  :  61  :             /* Use the segment */  62  :  63  :             /* Must specify also a letter (to write to the buffer) */  64  :             if (argc < 3) exit(-1);  65  :  66  :             user = (char)argv[2][0];  67  :  68  :             /* Grab the shared memory segment */  69  :             shmid =  shmget  (MY_SHM_ID, 0,   );  70  :  71  :             /* Attach to it */  72  :             block = (MY_BLOCK_T *)  shmat  (shmid, (const void *)0, 0);  73  :  74  :             for (i = 0 ; i < 2500 ; i++) {  75  :  76  :               /* Give up the CPU temporarily */  77  :               sleep(0);  78  :  79  :               /* Grab the semaphore */  80  :               sb.sem_num = 0;  81  :               sb.sem_op = -1;  82  :               sb.sem_flg = 0;  83  :               if (  semop  (block->semID, &sb, 1) != -1) {  84  :  85  :                 /* Write our letter to the segment buffer  86  :                  * (only if we have the semaphore).  This  87  :                  * is our critical section.  88  :                  */  89  :                 block->string[block->counter++] = user;  90  :  91  :                 /* Release the semaphore */  92  :                 sb.sem_num = 0;  93  :                 sb.sem_op = 1;  94  :                 sb.sem_flg = 0;  95  :                 if (  semop  (block->semID, &sb, 1) == -1) {  96  :  97  :                   printf("Failed to release the semaphore\n");  98  :  99  :                 }  100  :  101  :               } else {  102  :  103  :                      printf("Failed to acquire the semaphore\n");  104  :  105  :               }  106  :  107  :             }  108  :  109  :             /* Were done, unmap the shared memory segment. */  110  :             ret =  shmdt  ((void *)block);  111  :  112  :           } else if (!strncmp(argv[1], "read", 6)) {  113  :  114  :             /* Here, well read the buffer in the shared segment */  115  :  116  :             shmid =  shmget  (MY_SHM_ID, 0, 0);  117  :  118  :             if (shmid != -1) {  119  :  120  :               block = (MY_BLOCK_T *)  shmat  (shmid, (const void *)0, 0);  121  :  122  :               /* Terminate the buffer */  123  :               block->string[block->counter+  1  ] = 0;  124  :  125  :               printf("%s\n", block->string);  126  :  127  :               printf("length %d\n", block->counter);  128  :  129  :               ret =  shmdt  ((void *)block);  130  :  131  :             } else {  132  :  133  :               printf("Unable to read segment.\n");  134  :  135  :             }  136  :  137  :           } else if (!strncmp(argv[  1  ], "remove", 6)) {  138  :  139  :             shmid =  shmget  (MY_SHM_ID, 0, 0);  140  :  142  :             if (shmid != -1) {  143  :  144  :               block = (MY_BLOCK_T *)  shmat  (shmid, (const void *)0, 0);  145  :  146  :               /* Remove the semaphore */  147  :               ret =  semctl  (block->semID, 0, IPC_RMID);  148  :  149  :               /* Remove the shared segment */  150  :               ret =  shmctl  (shmid, IPC_RMID, 0);  151  :  152  :               if (ret == 0) {  153  :  154  :                 printf("Successfully removed the segment.\n");  155  :  156  :               }  157  :  158  :             }  159  :  160  :           } else {  161  :  162  :             printf("Unknown command %s\n", argv[1]);  163  :  164  :           }  165  :  166  :         }  167  :  168  :         return 0;  169  :       } 
end example
 
Note  

The key point of Listing 17.10 is that reading or writing from memory in a shared memory segment must be protected by a semaphore. Other structures can be represented in a shared segment, such as a message queue. The queue doesn t require any protection because it s protected internally.

Now let s look at an example run of our application shown in Listing 17.10. We create our share memory segment and then execute our use scenarios one after another (quickly). Note that we specify two different characters to differentiate which process had control for that position in the string. Once complete, we use the read command to emit the string (a snippet is shown here).

 $  ./shmexpl create  Creating the shared memory segment     $  ./shmexpl use a  &     $  ./shmexpl use b  &     [1] 18254     [2] 18255     [1]+ Done     [2]+ Done     $  ./shmexpl read  aaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbaaabbb...     length 5000         $  ./shmexpl remove  Successfully removed the segment.     $ 

Note that in some cases, you can see an entire string of a s and then all the b s. It all comes down to executing the use cases quickly enough so that they each compete for the shared resource.




GNU/Linux Application Programming
GNU/Linux Application Programming (Programming Series)
ISBN: 1584505680
EAN: 2147483647
Year: 2006
Pages: 203
Authors: M. Tim Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net