Tutorial-9 (IPC-Shared Memory)
Tutorial-9 (IPC-Shared Memory)
The aim of this tutorial is to show you how the processes can communicate among themselves using
the Shared Memory regions. Shared Memory is an efficient means of passing data between programs.
One program will create a memory portion, which other processes (if permitted) can access. A shared
segment can be attached multiple times by the same process. A shared memory segment is described
by a control structure with a unique ID that points to an area of physical memory. In this tutorial the
following issues related to shared memory utilization are discussed:
Creating a Shared Memory Segment
Controlling a Shared Memory Segment
Attaching and Detaching a Shared Memory Segment
Shared memory is a feature supported by UNIX System V, including Linux, SunOS and Solaris. One
process must explicitly ask for an area, using a key, to be shared by other processes. This process will
be called the server. All other processes, the clients that know the shared area can access it. However,
there is no protection to a shared memory and any process that knows it can access it freely. To protect
a shared memory from being accessed at the same time by several processes, a synchronization
protocol must be setup. A shared memory segment is identified by a unique integer, the shared
memory ID. The shared memory itself is described by a structure of type shmid_ds in header file
sys/shm.h. To use this file, files sys/types.h and sys/ipc.h must be included. Therefore, your program
should start with the following lines:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
If a client wants to use a shared memory created with IPC_PRIVATE, it must be a child process of the
server, created after the parent has obtained the shared memory, so that the private key value can be
passed to the child when it is created. For a client, changing IPC_CREAT | 0666 to 0666 works fine. A
warning to novice C programmers: don't change 0666 to 666. The leading 0 of an integer indicates that
the integer is an octal number. Thus, 0666 is 10110110 in binary. If the leading zero is removed, the
integer becomes six hundred sixty six with a binary representation 1111011010. Server and clients can
have a parent/client relationship or run as separate and unrelated processes. In the former case, if a
shared memory is requested and attached prior to forking the child client process, then the server may
want to use IPC_PRIVATE since the child receives an identical copy of the server's address space
which includes the attached shared memory. However, if the server and clients are separate processes,
using IPC_PRIVATE is unwise since the clients will not be able to request the same shared memory
segment with a unique and unknown key.
4. Keys:
UNIX requires a key of type key_t defined in file sys/types.h for requesting resources such as shared
memory segments, message queues and semaphores. A key is simply an integer of type key_t;
however, you should not use int or long, since the length of a key is system dependent.
There are three different ways of using keys, namely:
A specific integer value (e.g., 123456)
A key generated with function ftok( )
A uniquely generated key using IPC_PRIVATE (i.e., a private key).
The first way is the easiest one; however, its use may be very risky since a process can access your
resource as long as it uses the same key value to request that resource. The following example assigns
1234 to a key: key_t SomeKey; SomeKey = 1234; The ftok( ) function has the following prototype:
key_t ftok ( const char *path, /* a path string */
int id /* an integer value */ );
Function ftok( ) takes a character string that identifies a path and an integer (usually a character) value,
and generates an integer of type key_t based on the first argument with the value of id in the most
significant position. Thus, as long as processes use the same arguments to call ftok( ), the returned key
value will always be the same. The most commonly used value for the first argument is ".", the current
directory. If all related processes are stored in the same directory, the following call to ftok( ) will
generate the same key value:
#include <sys/types.h>
#include <sys/ipc.h>
key_t SomeKey;
SomeKey = ftok(".", 'x');
After obtaining a key value, it can be used in any place where a key is required. Moreover, the place
where a key is required accepts a special parameter, IPC_PRIVATE. In this case, the system will
generate a unique key and guarantee that no other process will have the same key. If a resource is
requested with IPC_PRIVATE in a place where a key is required, that process will receive a unique
key for that resource. Since that resource is identified with a unique key unknown to the outsiders,
other processes will not be able to share that resource and, as a result, the requesting process is
guaranteed that it owns and accesses that resource exclusively.
5. Attaching a Shared Memory Segment to an Address Space – shmat( ):
Suppose process 1, a server, uses shmget( ) to request a shared memory segment successfully. That
shared memory segment exists somewhere in the memory, but is not yet part of the address space of
process 1. Similarly, if process 2 requests the same shared memory segment with the same key value,
process 2 will be granted the right to use the shared memory segment; but it is not yet part of the
address space of process 2. To make a requested shared memory segment part of the address space of a
process, use shmat( ). After a shared memory ID is returned, the next step is to attach it to the address
space of a process. This is done with system call shmat( ). The use of shmat( ) is as follows:
System call shmat( ) accepts a shared memory ID, shm_id, and attaches the indicated shared memory
to the program's address space. The returned value is a pointer of type (void *) to the attached shared
memory. Thus, casting is usually necessary. If this call is unsuccessful, the return value is -1.
Normally, the second parameter is NULL. If the flag is SHM_RDONLY, this shared memory is
attached as a read-only memory; otherwise, it is readable and writable.
In the following server's program, it asks for and attaches a shared memory of four integers.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int shm_id;
key_t mem_key;
int *shm_ptr;
if (shm_id < 0) {
printf("*** shmget error (server) ***\n");
exit(1);
}
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int shm_id;
key_t mem_key;
int *shm_ptr;
mem_key = ftok(".", 'a');
if (shm_id < 0)
{
printf("*** shmget error (client) ***\n");
exit(1);
}
Note that the above code assumes the server and client programs are in the current directory. In order
for the client to run correctly, the server must be started first and the client can only be started after the
server has successfully obtained the shared memory. Suppose process 1 and process 2 have
successfully attached the shared memory segment. This shared memory segment will be part of their
address space, although the actual address could be different (i.e., the starting address of this shared
memory segment in the address space of process 1 may be different from the starting address in the
address space of process 2).
6. Detaching and Removing a Shared Memory Segment - shmdt( ) and shmctl( ):
System call shmdt( ) is used to detach a shared memory. After a shared memory is detached, it cannot be
used. However, it is still there and can be re-attached back to a process's address space, perhaps at a
different address. To remove a shared memory, use shmctl( ). The only argument to shmdt( ) is the shared
memory address returned by shmat( ). Thus, the following code detaches the shared memory from a
program:
shmdt (shm_ptr);
where shm_ptr is the pointer to the shared memory. This pointer is returned by shmat( ) when the shared
memory is attached. If the detach operation fails, the returned function value is non-zero.
To remove a shared memory segment, use the following code:
shmctl (shm_id, IPC_RMID, NULL);
where shm_id is the shared memory ID. IPC_RMID indicates this is a remove operation. Note that after
the removal of a shared memory segment, if you want to use it again, you should use shmget( ) followed by
shmat( ).
Program Examples:
Two different processes communicating via shared memory we develop two programs here that illustrate
the passing of a simple piece of memory (a string) between the processes if running simultaneously:
/* shm_server.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
main()
{
char c;
int shmid;
key_t key;
char *shm, *s;
exit(0);
}
/* shm_client.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
main()
{
int shmid;
key_t key;
char *shm, *s;
/*
* We need to get the segment named "5678",
* created by the server.
*/
key = 5678;
/*
* Locate the segment.
*/
if ((shmid = shmget(key, SHMSIZE, 0666)) < 0) {
perror("shmget");
exit(1);
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/*
* Now read what the server put in the memory.
*/
for (s = shm; *s != NULL; s++)
putchar(*s);
putchar('\n');
/*
* Finally, change the first character of the segment
* to '*', indicating we have read the segment.
*/
*shm = '*';