The fastest way of getting data from process A to process B is by using the same piece of memory. Once things are set up so that more than one process can address the same memory space, the kernel is no longer involved in the passing of the data. In other words, no system calls need to be executed in order to manipulate data.
Of course, something needs to guard the access to the shared memory. For this purpose, semaphores are invented.
Shared memory is not really complicated. What actually happens, is summarized in the following steps:
As can be read in the last couple of sentences of the previous section, shared memory objects are kernel persistent. So chances are that your application needs to take into account the fact that the shared memory object is already created and just needs opening. So, creating or opening a shared memory object is done by the same function. Note that creating or opening does not mean getting access. That is done by shmat(), as discussed in the section hereafter.
int shmget ( key_t key, size_t size, int shmflag );
key_t key | The key, a value to identify the semaphore set system-wide. Can also be IPC_PRIVATE, which will strip the third parameter int semflg, so only the permission part is left. |
int size | The size (in chars) which you want the piece of shared memory to be. Pass 0 if you are just opening, not creating. |
int shmflag | This parameter is divided in parts, which must be glued together with the bitwise OR operator ( | ). One part can be IPC_CREATE. Or IPC_CREATE|IPC_EXCL. The other part is the permission part. It consists of nine bits which represent the wellknown permission bits rwxrwxrwx. For more information, check the examples. |
On success, shmget() will return an integer. This integer is needed when manipulating the object with shmat(), shmdt() and shmctl().
Some tips: create the key with ftok(const char *pathname, int proj_id) and as the pathname, use the path and filename of your binary. When any of the calls like shmget return an error, print out the key. The shared memory area can then always be cleaned up with the ipcrm command, see below.
In the examples below, it is assumed that the following macro and declarations have been made:
#define DONTCARE 0 /* to indicate that a parameter is ignored */ key_t shmem_key = 1234; /* the key for our shared memory object */ int n_shmem_id; /* unique value for a shared memory object */ int n_shmem_size = 16; /* the size of the shared memory object */
Create a shared memory object with a size of 32 bytes. The 3rd and last parameter gives all rights to the creator (the first six). It gives read rights to the group of the creator and others (the second and third four). Remember that the prefixed 0 (zero) tells your compiler that the number is octal.
n_shmem_id = shmget(IPC_PRIVATE, n_shmem_size, 0644);
Create a shared memory object. If creating doesn't succeed, because it already exists with the same key, -1 is returned and errno is set to EEXIST.
n_shmem_id = shmget(shm_key, n_shmem_size, IPC_CREAT | IPC_EXCL | 0644);
Open a shared memory object. If it does not exist, -1 is returned and errno is set to ENOENT.
n_shmem_id = shmget(shm_key, DONTCARE, DONTCARE);
Open the shared memory object with key shm_key. If opening doesn't succeed, because the object does not exist, try to create it.
n_shmem_id = shmget(shm_key, n_shmem_size, IPC_CREAT | 0644);
If we want to use the shared memory, we need a pointer to the first entry. This pointer is obtained by calling:
char* shmat ( int shmid, char* shmaddr, int shmflg )
int semid | The unique identifier for the shared memory object. This integer was returned by shmget(). |
char* shmaddr | Just pass NULL here. If you really want it, you can specify an offset value which will return the pointer to the address that is a certain amount of bytes away from the starting address. Check the man shmat page. |
int shmflg | Pass 0 here, or pass SHM_RDONLY so that the calling process only has read rights to the block of shared memory. Also, the constants SHM_RND and SHMLBA can be passed; they influence the results of passing a non-NULL pointer as the second parameter. |
In the example below, it is assumed that the shared memory object shmem_id is open and that a pointer-to-char ptr_shmem is declared. The example obtains the pointer to the start of the block of shared memory.
ptr_shmem = shmat(shmem_id, NULL, 0); if(ptr_shmem == (void*) -1) { /* do some error handling here */ }
When a process is finished with a shared memory block, it detaches the block with:
int shmdt ( char *shmaddr)
When a process exits, all shared memory objects currently attached to that process are implicitly detached.
Detaching is not the same as deleting. Besides others, deletion is accomplished by calling the following system call:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Three commands are provided. Instead of explaining each, here are some examples. They assume that the shared memory object shmem_id is opened and that the following declarations have been made:
struct shmid_ds buffer; /* assuming it's filled with necessary data */ int retval; /* to put the return value of shmctl() in for error checking */
The following piece of code will destroy the shared memory object that is identified by the integer shmem_id:
retval = shmctl(shmem_id, IPC_RMID, NULL); if(retval == -1) { /* do some error checking */ }
Each shared memory object has an associated kernel structure struct shmid_ds. The members that are important here, are shm_perm.uid, shm_perm.gid and shm_perm.mode. The following line of code sets these members to the corresponding members of the third parameter. Also, the member shm_ctime value is updated to the current time.
retval = shmctl(shmem_id, IPC_SET, &buffer);
The following line of code copies the kernel structure to the struct that is passed in the third parameter:
retval = shmctl(shmem_id, IPC_STAT, &buffer);
To get an overview of all shared memory areas, use the command ipcs. Output could look like the following:
------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x4306f4ef 131076 telis497 666 95436 1 0x43435049 163845 nobody 666 65536 0 ------ Semaphore Arrays -------- key semid owner perms nsems 0x430610b1 0 bartvk 666 1 0x4306f4ef 32769 telis497 666 1 0x43435049 65538 nobody 666 3 0x4306f48f 131076 bartvk 666 1 ------ Message Queues -------- key msqid owner perms used-bytes messages
To remove shared memory areas that are left over because a process crashed or something, use the command:
# ipcrm -M key
Or when you'd like to use the ID:
# ipcrm -m 131076