NEXT UP previous
Next: Sockets

System V IPC

In order to provide compatibility with other systems, Linux also provides an option to support the three System V IPC mechanisms: shared memory, messages and semaphores. Each of these has approximately the same kind of interface so we'll just briefly look at one of them - shared memory.

The use of pipes and FIFOs still suffers from one disadvantage and that is the amount of data copying and processor context switching that goes on to transfer the data.

Shared memory overcomes this problem by making the same block of memory simultaneously visible in the address spaces of two or more processes. This means that as soon as one process has written some information into the memory, it is immediately available to the other processes which share this memory block without any further copying operations taking place. Before any process can attach to a shared memory segment it first needs to be created. This is done with the shmget() call, which has the prototype:

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

	int shmget(key_t key, int size, int flags);

where size is the number of bytes required (rounded up to the smallest whole number of memory pages), key is a numeric value (which for a shared memory segment has a similar role to a file's name), and flags is a set of bit masks ORed together to specify access permissions and creation flags when a sheared memory segment is created (rather like a combination of the second and third parameters to an open() system call).

A value of IPC_CREAT as part of the flags parameter will cause a shared memory segment associated with the given key to be created if it does not already exist. Including the IPC_EXCL flag will cause a shmget() call to fail if a shared memory segment with the specified key already exists.

Using a value of IPC_PRIVATE in the key field is a way of creating a shared memory segment without worrying about whether or not the segment already exists.

The return value from shmget() is a shared memory segment ID which is similar in concept to a file descriptor returned by open(), in that the value needs to be passed into the other calls related to the segment. The big difference, however, is that any process which has the value of a shared memory segment ID can access the segment, not just descendant processes as in the file descriptor case.

A simple example program to create a shared memory segment could be:

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

	#define SHM_SIZE 4096
	#define SHM_FLAGS IPC_CREAT | 0644

	main()
	{
		int shmid;

		shmid = shmget(IPC_PRIVATE, SHM_SIZE  SHM_FLAGS);

		if (shmid==-1)
			fatal("creating shared memory segment");

		printf("shared memory segment ID = %d\n", shm_id);
	}



	fatal(char *mess)
	{
		fprintf(stderr, "Error: %s\n", mess); 
		exit(1)
	}

The status of any System V IPC resources can be examined, once created, with the ipcs command. A -m switch to the command gives information about shared memory segments, a -s switch lists semaphore arrays and -q is for message queues. If no switch is specified then all three resource lists will be displayed.

If the program in the previous listing is called make_shm then it can be used and its results examined with the following command sequence:

	$ make_shm
	shared memory segment ID = 1408
	$ ipcs

	------ Shared Memory Segments --------
	shmid	owner	perms	bytes	nattch	status
	1408	pc	644	4096	0

      	------ Semaphore Arrays -------
        semid	owner	perms	nsems	status

      	------ Message queues --------
	msqid	owner	perms	used-bytes	messages

Once the shared memory segment exists it can be attached to the memory of a running process with the shmat() call, the prototype for which is:

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

	char *shmat(int shmid, char *shmaddr, int flags);

where shmid is the shared memory segment ID returned by the shmget() call, shmaddr is the address where you want the shared memory segment to be attached and flags allows you to specify that you want to attach the segment read-only (using the SHM_RDONLY flag) instead of read-write. Normally, you will not want to specify your own shmaddr and you can get the system to pick an address for you by passing the parameter value zero.

The return value from shmat() is a pointer (C type pointer) to the shared memory segment, which you can then use just like a pointer to any other block of memory.

When you have finished with a shared memory segment, you can detach it from your process with a shmdt() call:

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

	int shmdt (char *shmaddr);

passing the memory address returned by the corresponding shmat() call as the shmaddr parameter.

The shmdt() call does not destroy the shared memory segment, even if no other process is attached to it. Indeed, anything written into the segment will remain intact so that subsequent shmat() calls will give access to the memory segment and to the data it contains.

If you actually want to destroy a shared memory segment (which you should do when you have really finished with it) this can be done with a control function call named shmctl():

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

	int shmctl(int shmid, int cmd, struct shmid_ds *buf);

This function is similar in concept to ioctl() and fcntl() in that it allows both read and write access to a structure of information related to a shared memory segment specified by shmid. The cmd parameter specifies the actual operation to perform and buf is a pointer to a shared memory structure which is used for passing data about as rcquired. The following cmd values are available:

IPC_STATcopy shared memory structure into buf
IPC_SETset shared memory structure from buf,
IPC_RMIDdestroy shared memory segment given by shmid.

An alternative way to destroy a shared memory segment is to use the command ipcrm:

	$ ipcrm shm shmid

where shmid is the shared memory segment ID given in the ipcs display. For example:

	$ ipcrm shm 1408

The following is a short program to demonstrate attaching the memory segment with ID = 1408 to the process, writing some data to the segment and then detaching it to await reading by some other process:

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

	#define SHM_ID 1408

	main()
	{
		char *shmaddr;

		if ((shmaddr = shmat(SHM_ID,  0,  0))==(char *)-1) 
			fatal("attaching shared memory segment");
		strcpy(shmaddr, "Some test message");

		if (shmdt(shmaddr)==-1) 
			fatal("detaching shared memory segment");
	}


	fatal(char *mess)
	{
		fprintf(stderr, "Error: %s\n", mess); 
		exit(1);
	}

Notice that, in common with the system calls, the shm functions all return -1 on error. Since shmat() actually returns a pointer to a block of memory it is necessary to cast the -1 value to char * in order to perform the comparison.

Though they operate on different data structures, the other System V IPC mechanisms (semaphores and messages) are very similar in concept and usage to shared memory, in that they each have a call to create the resource in the first place; they also have calls for access and control operations:

shared memorysemaphoresmessages
creationshmget()semget()msgget()
accessshmat()semop()msgsnd()
shmdt()msgrcv()
controlshmctl()semctlmsgctl()

NEXT UP previous
Next: Sockets