Operating Systems


UNIX Interprocess Communications

William J. Freda


William J. Freda is a senior consultant with Automated Concepts Inc. Specializing in UNIX/C applications in a relational database environment, William has had a variety of assignments throughout AT&T and Bell Laboratories. You can reach him at 90 Woodbridge Center Drive, Woodbridge, N.J. 07095, (201) 602-0200

Interprocess Communication (IPC) enables two or more processes to exchange information, share data, and synchronize execution. The basic interprocess communications facilities are signals, pipes, and named pipes. The more advanced facilities are semaphores, shared memory, and message queues. In this article, I describe each with corresponding advantages and disadvantages.

Signals

Signals are sent to processes when a particular event occurs or when some type of unrecoverable error occurs. Typically, user processes will trap most of these signals to perform some clean-up work before exiting the program. User processes can also send signals to each other via the kill (2) system call, but this is limited to descendants, or children, of the original proccess and to the superuser.

Pipes

A pipe is a unidirectional communication channel open between a parent and child process. A process can open a pipe to another process for reading or writing but not both. This limitation makes pipes unsuitable for processes that need to communicate bidirectionally.

Named Pipes

A named pipe is a FIFO (first in, first out) file. One process writes into the named pipe and the second process reads it. Since a named pipe is a regular file, its access permission can be set to allow all to use it. Thus, a named pipe is not limited to parent and child processes. One disadvantage of named pipes is that user processes can't be selective about what type of information they want to read from the pipe. In other words, if you were looking for a particular record in a named pipe, you would have to sequentially read the pipe until you found the record. Once information is read from a named pipe it is lost. In adddition, only one process should have a named pipe open ffor reading at a time. If more than one process has a named pipe open, the kernel will effectively toggle between the processes, giving one process one record, the second process the next record and so on.

Semaphores

Semaphores enable processes to synchronize execution. A semaphore is a positive integer that supports two possible operations: P and V. The P operation decrements the value of a semaphore, the V operation increments it. The semaphore's value must always be greater than or equal to 0. If a process attempts to decrement the semaphore's value with a P operation when its value is 0, the kernel puts the process to sleep until the operation can complete successfully. Semaphores have the following advantages over signals:

Shared Memory

Shared memory enables processes to attach a common data area to share information. Once attached, the shared memory segment can be accessed the same way you access a memory block obtained from malloc(3C). Typically, you use a semaphore to ensure that only one proccess at a time writes to shared memory.

Message Queues

A message queue enables two or more processes to exchange information. Message queues have some advantages over pipes and named pipes:

These IPC facilities are beneficial because they enable you to split tasks among multiple processes. Say, for example, that you have an application that reads records from a tape, validates them, then updates several tables in the database. Using the IPC facilities, you can split the application into two separate processes: one that reads and validates records from the tape and one that updates the database. With this scheme, the second process updates the database while the first process reads and validates the next record. If this could cut processing time by 1 percent with 100,000 records on a tape (10ms per record), the net reduction in processing time would be 1,000 seconds, or 16 minutes and 40 seconds.

A message queue enables two or moe processes to exchange information, ranging from a simple character string to an array of C structures.

Before a process can send or receive messages, it must obtain a message queue identifier (msqid) from the UNIX kernel. The msqid, which is similar to a file descriptor, is basically an index into a set of kernel tables. The msgget (2) system call is used for this purpose (see Listing 1) .

Synopsis

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
int msgget(key, msgflg)
key_t key; int msgflg;

Argument Description

key is a user-chosen name for a message queue. If key is set equal to IPC_PRIVATE, it informs the kernel that this is a private message queue. Private message queues are known only to the calling process and any child processes. This is roughly equivalent to opening a bidirectional pipe between two processes.

msgflg is an integer value that contains the access permissions of the message queue ORed with the control flags IPC_CREAT and/or IPC_EXCL. The access permissions for a message queue are similar to the access permission for a regular file. The IPC_CREAT control flag instructs msgget() to create a message queue if one doesn't already exist. The IPC_EXCL control flag is used with IPC_CREAT to ensure that a new message queue is created and to return an error if one already exists.

Upon successful completion, msgget() returns a message queue identifier. Otherwise, msgget() returns -1 and places the error number in the external integer errno.

Possible Errors

EACCES — A message queue identifier already exists for key, but access permission is denied to this process.

ENOENT — The message queue needs to be created but msgflg did not contain IPC_CREAT.

ENOSPC — The maximum number of message queues system wide has been reached. This error rarely occurs.

EEXIST — A message queue identifier already exists for key. This occurs when msgflg contained IPC_CREAT IPC_EXCL.

The example in Listing 1 creates a (possible existing) message queue with owner and group read/write permission. The example in Listing 2 creates a new message queue with owner and group read/write permission. It also notifies the user with an appropriate error message if the message queue already exists.

Message Queue Operations

Sending Messages

Sending messages is accomplished using the msgsnd(2) system call.

Synopsis

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

int msgsnd(msqid, msgp, msgsz, msglf)
int msqid;
struct mssg *msgp;
int msgsz, msgflg;

Argument Description

msqid is a message queue identifier obtained from a previous call to msgget(2). msgp is a pointer to a user-defined structure:

struct mssg {
long mtype; /* Message type */
char mtext[LENGTH]; /* Message text */
   }
mtype — Can be used to assisn the message a particular type or classification. The receiving process uses mtype to select a particular message on queue without having to sequentially read each message.

mtext — The text of the message. LENGTH is any size your application requires up to a system-defined maximum.

msgsz — The length of the message contained in mtext.

msgflg — A control flag that contains either 0 or IPC_NOWAIT. If the message queue is full, the kernel will normally put the process to sleep until the message queue has sufficient room to place the message.

The IPC_NOWAIT control flag disables this feature, and msgsnd() returns immediately if the message queue is full.

Upon successful completion, msgsnd() returns 0. Otherwise, msgsnd() returns -1 and places the error number in the external integer errno. See the example in Listing 3.

Possible Errors

EINVAL

1) Msqid is an invalid message queue identifier

2) Mtype is less than 1

3) Msgsz is less than 0 or greater than the system limit

EACCESS — Access permission denied.

EAGAIN — The message queue is full. this only happens when msgflg contains IPC_NOWAIT.

EFAULT — Only a program bug can cause this error. Msgp points to an illegal address.

EINTR — The process was asleep waiting to send a message and an interrupt occurred.

EIDRM — The process was asleep waiting to send a message and someone removed the message queue from the system.

Receiving Messages

Receiving messages is accomplished using the msgrcv(2) system call. See the example in Listing 4.

Synopsis

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
int msgrcv(mspid, msgp, msgsz,
          msgtyp, msgflg)
int msqid;
struct mssg *msgp;
int msgsz;
long msgtyp;
int msgflg;

Argument Description

msqid — A message queue identifier obtained from a previous call to msgget (2) .

msgp — A pointer to the user-defined structure described above.

msgsz — The length of the message buffer mtext. This prevents the kernel from inadvertently overwriting the buffer.

msgtyp — A particular message type you wish to receive. If it contains 0 the first message on the message queue is returned regardless of the message type.

msgflg — A control flag that contains either 0, IPCC_NOWAIT, MSG_NOERROR , o r IPC_NOWAIT ORed with MSG_NOERROR. The kernel will normally put the process to sleep if the message queue is empty or there is no message of msgtyp currently on queue. The IPC_NOWAIT control flag disables this feature and causes msgrcv() to return immediately. If the message is too long, msgrcv() will normally return an error. The MSG_NOERROR control flag disables this feature and msgrcv() will silently truncate any message that is too long.

Upon successful completion, msgrcv() returns the number of bytes places in mtext. Otherwise, msgrcv() returns -1 and places the error number in the external integer errno.

Possible Errors

EINVAL

1) Msqid is an invalid message queue identifier

2) Msgsz is less than 0

EACCESS — Access permission denied.

E2BIG — The message on queue is longer than msgsz. This error is not returned if msgflg contains MSG_NOERROR.

ENOMSG — There is no message of the desired type on queue. This only happens when msgflag contains IPC_NOWAIT.

EFAULT — Only a program bug can cause this error. Msgp points to an illegal address.

EINTR — The process was asleep waiting to receive a message and an interrupt occurred.

EIDRM — The process was asleep waiting to receive a message and someone removed the message queue from the system.

Controlling Message Queues

In addition to sending and receiving messages, your application may need to exercise some degree of control over a message queue. The msgctl(2) system call is used for this purpose. It performs the following functions:

Synopsis

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

int msgctl (msqid, cmd, buf)
int msqid, cmd;
struct msqid_)ds *buf;

Argument Description

msqid — A message queue identifier obtained from a previous call to msgget (2).

cmd — One of the following commands:

IPC_STAT — Gather statistics as detailed above.

IPC_SET — Change the access permissions.

IPC_RMID — Remove the message queue from the system.

Upon successful completion, msgctl() returns 0. Otherwise msgctl() returns -1 and places the error number in the external integer errno.

Possible Errors

EINVAL

1) Msqid is an invalid message queue identifier

2) Cmd is an invalid command

EACCESS — Access permissions denied.

EPERM — This error occurs whenever cmd is IPC_SET or IPC_RMID and someone other than the owner or the super-user is attempting to change the access permissions or remove the message queue.

EFAULT — Only a program bug can cause this error. Buf points to an illegal address.

Quite frankly, the only facility of msgctl() that I have found useful is IPC_RMID, whicch removes a message queue from the system (see Listing 5) . If I want detailed information about a message queue or any other IPC facility, I use the UNIX ipcs command, which dumps out the statistics described above.

The sample program in Listing 6 could easily fit into a number of different applications. With this program, users can send one-line messages to each other. The sen_mssg() function prompts the user to enter the login name of the user to which he or she wants to send a message. send_mssg() then extracts the UNIX userid by querying the password file using the library function getpwent (3C). The message is then inserted into the message queue via msgsnd(2) with the message type set equal to the userid.

Placing calls to recv_mssg() in different areas of the application gives the users the illusion that the message has been received in near realtime. The call to msgrcv(2) uses the IPCC_NOWAIT control flag and will return immediately to the caller if no message is on queue.

Summary

Message queues allow multiple processes to exchange information bidirectionally. Since a message queue is a UNIX kernel data structure and thus part of the operating system, this information is passed between processes without the need for any disk I/O. In addition, processes can select a particular message type without having to sequentially read each message. message queues eliminate the need for consuming processor cycles polling for a particilar message to arrive. The UNIX kernel takes care of this for you by putting your process to sleep and waking it when the message arrives.

By recognizing areas in an application where tasks could be performed concurrently, IPC facilities such as message queues allow you to perform the most work in the least amount of time.