System V IPC is meant to provide and entire IPC mechanism. As such, it is more heavyweight than BSD mmap, and provides three methods of communication: message queues, semaphores, and shared segments. Like BSD mmap, System V IPC uses files to identify shared segments. Unlike BSD, System V uses these files only for naming. Ther contents have nothing to do with the initialization of the shared segment.
The three System V IPC mechanisms share some common ground when it comes to creation and permissions. This section will attempt to give you an overview the creation process.
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(char *pathname, char proj);
IPC_PRIVATE
struct ipc_perm
{
key_t key;
ushort uid; /* owner euid and egid */
ushort gid;
ushort cuid; /* creator euid and egid */
ushort cgid;
ushort mode; /* lower 9 bits of access modes */
ushort seq; /* sequence number */
};
IPC_CREAT
IPC_EXCL
All three IPC methods provided by the System V IPC are assocated with
system-wide integers, called keys. While it is not required, these
keys are traditionnaly obtained via ftok(2).
ftok(2) takes a
filename and a project argument to generate a unique key that
describes an instance of an IPC mechanism. The project argument allows
you to have multiple IPC mechanisms associated with the same filename.
You may also specify a key of IPC_PRIVATE that will be used only by
your process and it's children.
When you request an IPC instance using an appropriate key value, you may specify the flags IPC_EXCL and IPC_CREAT. If you specify IPC_CREAT, if a segment for the key does not exist, it is created. If you also specify IPC_EXCL, you are guanteed a new segment or an error will be returned. (It doesn't not prevent others from requesting the segment as well).
Permissions are set upon requesting a segment, and are in the traditional UNIX rwx bitwise scheme. That is:
#define PERM_UREAD 0400
#define PERM_UWRITE 0200
#define PERM_GREAD 0040
#define PERM_GWRITE 0020
#define PERM_OREAD 0004
#define PERM_OWRITE 0002
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
IPC_STAT
IPC_SET
IPC_RMID
struct shmid_ds;
Shared segments in System V are a bit different than those obtained
with BSD's mmap. First of all, while you do need a key for a segment
which is usually obtained by running ftok(2) on some file in the
filesyetem, System V shared segments exist only in memory. Because of
this, System V shared segments must persist after the termination of a
process. It is the responsibility of the programmer to ensure that all
of his segments are removed from the system, otherwise his memory
leaks become system wide.
To create a shared memory segment, you must use the
shmget(2) system call. It takes a key, a size, and a flag
paramater and returns an integer id for your segment. The flag can
be combinations of IPC_CREAT and IPC_EXCL,
as well as the permissions bits on the segment as described above.
Once the segment is created and you have it's id, you must attach it,
or map it into
memory. This is done with shmat(2). shmat works
much the same as mmap in that you can specify an address to map to,
but the address must be a multiple of SHMLBA, unless you specify
SHM_RND in the flags paramater.
Various operations can be performed using shmctl(2)
IPC_STAT can be used to fill in the struct shmid_ds
structure. You can then set various fields (including changing
ownership and premissions of ipc_perm) using IPC_SET.
But perhaps the most important ctl operation is IPC_RMID.
When all of your programs are done using a segment, you MUST shmctl
it with IPC_RMID (the shmid_ds structure may be NULL), or
else the segment will remain in memory forever.
Message queues provide a memory based FIFO between two processes. The primary difference between a System V message queue and a socket or named pipe is that message queues may have multiple processes reading and writing from and to them, or no readers at all.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
int msgsnd(int msgid, struct msgbuf *msgp, int msgsz,
int msgflg);
int msgrcv(int msgid, struct msgbuf *msgp, int msgsz,
long msgtyp, int msgflg );
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[msgsz]; /* message data */
};
MSGMAX
MSGMNB
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
IPC_STAT
IPC_SET
IPC_RMID
struct msgid_ds;
Much like for shared memory, you must create a message queue using
the
msgget(2) system call. It takes a key and a flag
paramater and returns an integer id for your segment. The flag can
be combinations of IPC_CREAT and IPC_EXCL,
as well as the permissions bits on the segment as described above.
The System V IPC provides two symmetric system calls for sending and
receiving messages: msgsnd and msgrcv
respectively.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget ( key_t key, int nsems, int semflg );
int semop ( int semid, struct sembuf *sops, unsigned nsops);
struct sembuf {
short sem_num; /* semaphore number: 0 = first */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
int semctl (int semid, int semnum, int cmd, union semun arg);
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short int *array; /* array for GETALL, SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
IPC_STAT
IPC_SET
IPC_RMID
GETALL
SETALL
SETVAL
struct semid_ds;
Semaphores are data structures that are used for synchronization between two or more processes. Basically they can be viewed as a single integer that represents the amout of resources avaialable. When a process wants a resource, it checks the value of the semaphore, and if it it nonzero, it decrements the appropriate number from the semaphore in accordance to the amout of resources it wishes to use. The kernel will block the process if the semaphore is zero or doesn't have a value high enough for the decrement. In System V IPC, you may request a set or an array of semaphores at once.
To create a set of semaphores, you must use the
semget(2) system call. It takes a key, a number of
semaphores to create, and a flag paramater and returns an integer
id for your segment. The flag can
be combinations of IPC_CREAT and IPC_EXCL,
as well as the permissions bits on the segment as described above.
Once you call semget and have the semaphore set's ID, you can perform
operations on this set using semop(2). The struct sembuf
has fields that are filled in to represent the requested action on the
semaphore. The first field specifies the number of the semaphore in
the set you wish to operate on. The second field (sem_op) contains the
operation you wish to do. If sem_op is positive, the value is added
to the semaphore (ie release of resources). If sem_op is less than
or equal to zero, semop(2) will block until the semaphore reaches
abs(sem_op). You may also specify various options in sem_flg,
including IPC_NOWAIT, which tells semop(2) NOT to block. When
SEM_UNDO is specified, all actions are undone when the current
process exits. Undo is guaranteed with private semaphores only.
Notice that unlike the other IPC operations, the third argument to
semctl is a union, not a simple struct. This is because the various
options must occurr on different data.
Various operations can be performed using semctl(2)
IPC_STAT can be used to fill in the struct semid_ds
structure. You can then set various fields (including changing
ownership and premissions of ipc_perm) using IPC_SET.
But perhaps the most important ctl operation is IPC_RMID.
When all of your programs are done using a semaphore, you MUST semctl
it with IPC_RMID (the shmid_ds structure may be NULL), or
else the semaphore will remain in memory forever. You may also set
and get specific values of the semaphore using GETVAL, SETVAL,
GETALL, and SETALL.