next up previous contents
Next: 4 Networking Principles Up: Unix Communication Facilities Previous: 2 Analysis & Design

3 Local IPC

This chapter gives on overview of the most common local UNIX interprocess communication facilities, including
  1. Signals
  2. Pipes (named and unnamed)
  3. Stream Pipes
  4. Messages
  5. Semaphores
  6. Shared Memory

This chapter describes these interprocess communication facilities that are restricted to the use between processes that are executed on one computer.

All ``local'' interprocess communication facilities rely on memory for the exchange of information. Processes in the UNIX model can normally not see/access the memory of another process (except for System V shared memory regions, described later in this chapter). Therefore the kernel (which has access to all memory) has to copy the data first from the user address space into some internal kernel buffers (caused by a ``write'' system call), and from there to the address space of another process (caused by a ``read'' system call from that process). Figure 5 illustrates this process.

figure353
Figure 5: The Principle of most local IPC Facilities

Semaphores and other related IPC facilities do not copy information, but modify memory that is controlled by the kernel.

How do processes get ``in touch'' with one another? Two basic approaches exist for this purpose:

The socket interface which provides distributed IPC facilities, but also IPC facilities that are only available between processes that run the same computer, is introduced in Chapter 6.1.

3.1 Signals

Most UNIX applications have to deal with signals. A good introduction in the terminology of signals is given in [Zlo91]:

[Signals] are a mechanism by which a process may be notified of, or affected by, an event occurring in the system. When the event that causes the signal occurs, the signal is said to be generated. When the appropriate action for the process in response to the signal is taken, the signal is said to be delivered. In the interim, the signal is said to be pending.

Signals are sometimes called ``software interrupts'', as they behave very similarly to hardware interrupts. Signals usually occur asynchronously (Figure 6). Signals can be sent from one process to another process (or to itself), or are generated by the kernel.

Signals that are not asynchronous are normally generated if an error occurs like an erroneous arithmetic operation.

A process can choose how to handle a specific signal. A signal can be treated in the following ways:

figure377
Figure 6: Execution of a Signal Handler

Originally the signal() system call was used to influence the behaviour of signals. The problem with the original implementation of signals was that they were unreliable, signals could be lost. A process also had to reinstall a signal handler every time it was invoked.

This was changed quite early by the AT&T and BSD version of UNIX, but unfortunately the changes are not compatible. The POSIX.1 standard, which defines a standard for reliable signals, is widely accepted in the UNIX world. Therefore only the signal-related functions of this standard are described here.

Every signal has a specific name. Table 3 states the signals which every POSIX-compliant system has to support. The system calls in Table 4 are useful for dealing with signals.

Signal Default Action Description
SIGABRT terminate w/core Abnormal termination, normally caused by abort().
SIGALRM terminate Timeout, caused e.g. by alarm().
SIGFPE terminate w/core Erroneous arithmetic operation (e.g. divide by zero).
SIGHUP terminate Hangup on controlling terminal.
SIGILL terminate w/core Invalid hardware instruction.
SIGINT terminate Interactive attention signal (interrupt).
SIGKILL terminate Termination. Cannot be caught or ignored.
SIGPIPE terminate Write on a pipe that is not open for reading by any process.
SIGQUIT terminate w/core Interactive termination signal (quit).
SIGSEGV terminate w/core Invalid memory reference.
SIGTERM terminate Termination signal.
SIGUSR1 terminate Application-defined signal 1.
SIGUSR2 terminate Application-defined signal 2.
Table 3: POSIX Signals

Function Description
kill() sends a signal to a process
raise() sends a signal
alarm() schedules an alarm
pause() suspends process execution
sigaction() examines and changes signal action
sigprocmask() examines and changes blocked signals
sigpending() examines pending signals
sigsuspend() waits for a signal
Table 4: Signal Functions

Some ``slow'' system calls like read()s from pipes are interrupted by a signal. They return the error code EINTR, and the program has the responsibility to issue the system call again if needed. As this is not always a desired behaviour, some systems allow an automatic restart of these slow system calls on a per-signal basis (option SA_RESTART, a Berkeley extension, with sigaction()).

Signal handling is useful in many contexts. An example is to signal another process that it is ready for a particular task or to do some clean-up operations like removing temporary files if a SIGINT signal is caught. The program local/signal.c illustrates the use of the described signal functions. A more detailed description of POSIX.1 signal handling can be found in [Zlo91, Chapter 5], [POSIX.1, Chapter 6], and [Stev92, Chapter 10].

In terms of interprocess communication signals can not transport large amounts of information from one process to another process. The advantage of signals is that they normally occur completely asynchronously. Signals were used in some of the performance measurement programs in Chapter 7 to synchronize different processes.

3.2 Pipes

Pipes are the oldest IPC mechanism in UNIX, therefore they are supported by all UNIX systems. Pipes are created with the pipe(int fd[2]) system call, which creates two new descriptors. These descriptors are connected, the first one is used for reading and the second one for writing. The result of a pipe system call can be seen in Figure 7.

figure451
Figure 7: The Result of the pipe() System Call

Pipes are normally useless in a single process. They become useful when a process wants to communicate with its children or vice versa. Children are created with the fork() system call. The result of a fork on a pipe is shown in Figure 8.

figure461
Figure 8: A Pipe after a fork() System Call

If the data should flow from the child to the parent, the child closes the read end of the pipe, and the parent process closes the write end. This results in Figure 9.

figure470
Figure 9: Resulting Pipe

The UNIX system guarantees that a certain number of bytes are written atomically to a pipe. This number of bytes varies, but is normally 4096 or 8192 bytes (the system constant PIPE_BUF should be used for portability). This can be used if several processes have to communicate with one process.

The use of pipes has two major drawbacks:

  1. the processes which want to communicate must have a common ancestor
  2. the data can only flow in one direction.

FIFOs (described next) can solve the first problem, while stream pipes (Chapter 3.4) solve the second one. Program local/pipe.c demonstrates the use of pipes.

3.3 FIFOs (named pipes)

FIFOs (for First In, First Out), sometimes also called ``named pipes'', solve the problem that processes have to be related when they wish to communicate. A FIFO is created with the system call mkfifo(const char *pathname, mode_t mode), or the shell command mkfifo.

FIFOs use the pathname to establish a connection. A special file in the file system is created with the permission bits mode which can be seen by all processes. A FIFO can then be used with the normal I/O functions like open(), read(), write() and close(). One or more processes may open a FIFO for reading and writing.

FIFOs are often created by shells and used by shell commands to pass data from one process to another. The use of FIFOs removes the necessity for temporary files. Another use for FIFOs is the exchange of data in a client/server application where the processes are not related (e.g. they are created dynamically from different processes). The programs local/fifoclient.c and local/fifoserver.c demonstrate the use of FIFOs.

FIFOs have the drawback that communication is still only possible in one direction. Either two FIFOs have to be used for communication in both directions, or stream pipes (described next). Another problem is the use of file names, as it may not be clear what happens if a program crashes and leaving a FIFO in the file system, without the other processes notifying the crash of the communication partner.

3.4 Stream Pipes

A so-called ``stream pipe'' is a bi-directional pipe. To obtain bi-directional data flow between parent and child, only a single stream pipe is required [Stev92]. Stream pipes can be named or unnamed.

In SVR4 every pipe is already a bi-directional pipe, so the normal pipe() system call creates always an unnamed stream pipe.

In BSD an unnamed stream pipe can be obtained via the int socketpair(int domain, int type, int protocol, int fd[2]) system call like this:

    int fd[2];
    int success;
    success = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);

For a description of socket parameters please refer to Chapter 6.1.

Named stream pipes are more difficult to establish. The function namedstreampipe() in the file local/namedstreampipe.c implements named stream pipes for BSD systems with the use of sockets. Named stream pipes are possible within SVR4, but have to be created by the superuser and are therefore not discussed further.

3.5 System V IPC

The System V IPC facilities were first added to System V. They include three different types of IPC with a very similar interface (see Table 5 for summary of system calls):

Message Queue Semaphore Shared Memory
system call to create or open msgget() semget() shmget()
system call for control operations msgctl() semctl() shmctl()
system calls for IPC operations msgsnd() semop() shmat()
msgrcv()   shmdt()
Table 5: Summary of System V IPC System Calls

A System V IPC object (a message queue, a set of semaphores, or a region of shared memory) has an unique identifier, a key. Keys are comparable to the file descriptors used for normal I/O, but they exist system wide, they are not related to processes. To access a certain System V IPC object the key has to be known. Key identifiers have to be selected carefully, otherwise conflicts between unrelated applications might arise. Message queues, semaphores and shared memory are using different name spaces, e.g. a key for a shared memory region is meaningless for semaphores and messages. To get unique keys the library function key_t ftok(char *pathname, char proj) can be used. This function converts the pathname of an existing accessible file and a ``project identifier'' (an arbitrary 8-bit value) into a (hopefully unique) key suitable for System V IPC.

Each IPC object has a set of IDs and permissions (reading and writing) like files in a file system. This allows control over the access to a specific IPC object. The owner ID and group ID specifies processes with owner or group access privileges.

All three forms of System V IPC have built-in limits. These may be changeable on some systems by reconfiguring the kernel, others have fixed limits. The maximum number of message queues, shared memory regions and semaphore sets that a process can have on the three different systems that are used (and described) for performance measurements in Chapter 7 was derived with the program environment/sysvipclimits.c (and adjusted according to the ipcs command). A word of caution: on some systems the number of available IPC objects per process is equal to the number of system wide available IPC objects. On other systems the system wide number of IPC objects is higher than the number of IPC objects a process can obtain.

3.5.1 Message Queues

Message queues are a linked list of messages stored within the kernel and identified by a message queue key [Stev92]. Message queues are used to send data from one process to another process. Every message can have a priority, and the receiver can specify the priority of the messages it is willing to receive. This can also be used to allow bi- or more-directional flow of data if every sender identifies its data with a different priority. The size of individual messages is often very limited, normally to about 2 to 4 Kb. Table 6 gives an overview of the system calls that can be used to handle message queues.

System Call Description
msgget() open an existing message queue or create a new queue
msgctl() performs various operations on a message queue, e.g. removing a message queue
msgsnd() place data onto a message queue
msgrcv() retrieve a message from a message queue
Table 6: System Calls for Handling Message Queues

The program local/message.c demonstrates the use of message queues by implementing the classical producer/consumer problem. Table 7 shows the maximum number of message queues a process may have at best on a variety of systems.

System Message Queues
SparcCenter 2000/Solaris 2.5 50
HP 9000/710/HP-UX 9.01 50
i586/Linux 1.2.9 128
Table 7: Maximum Number of Message Queues per Process

3.5.2 Semaphores

Semaphores are used for managing a fixed amount of shared resources. The UNIX System V IPC semaphores have more capabilities than normal abstract semaphores [Stev92]:

Table 8 gives an overview of the system calls that can be used to handle semaphore sets.

System Call Description
semget() obtain a semaphore key
semctl() performs various operations on a semaphore set, e.g. removing a semaphore set
semop() perform atomically an array of operations on a semaphore set
Table 8: System Calls for Handling Semaphore Sets

A problem with System V IPC semaphores is that the creation of semaphores is independent of their initialization. This can lead to race conditions, an atomic system call that handles this problem would be desirable.

The program local/semaphore.c shows how semaphores can be used to coordinate two different processes. Table 9 shows the maximum number of of semaphore sets a process may have at best on a variety of systems.

System Semaphore Sets
SparcCenter 2000/Solaris 2.5 10
HP 9000/710/HP-UX 9.01 64
i586/Linux 1.2.9 128
Table 9: Maximum Number of Semaphore Sets per Process

3.5.3 Shared Memory

Shared memory overcomes the normal isolation between process address spaces. It allows windows of memory to be shared among a number of processes (possibly at different address spaces). The access to the shared memory is not interlocked, therefore the processes using shared memory normally have to coordinate the access themselves for example with semaphores. Shared memory is accessed and manipulated using normal instructions. Table 10 gives an overview of the system calls that can be used to handle shared memory regions.

System Call Description
shmget() obtain a shared memory key
shmctl() performs various operations on a semaphore set, e.g. removing a shared memory region
shmat() attach a shared memory region to the address space
shmdt() detach a shared memory region from the address space
Table 10: System Calls for Handling Shared Memory Regions

The maximum number of shared memory regions a process can have on each of the computer systems used varies greatly. (see Table 11). The program local/shared.c demonstrates the use of shared memory.

System Shared Memory Regions
SparcCenter 2000/Solaris 2.5 6
HP 9000/710/HP-UX 9.01 120
i586/Linux 1.2.9 128
Table 11: Maximum Number of Shared Memory Regions per Process

3.5.4 System V IPC Problems

System V IPC facilities often create problems for system maintenance, as the IPC structures are system wide without a reference count. If a program crashes before the IPC structures are freed, or the program which uses these IPC facilities has a bug, they remain in the system till it is rebooted. This causes a problem, as the maximum number of semaphore sets etc. is limited.

For this reason two programs can be used to get information about System V IPC in a system or to delete them:

3.6 Summary

A large number of different local IPC facilities are available in UNIX. Signals, pipes, FIFOs, stream pipes, message queues, semaphores, and shared memory were described. Signals allow asynchronous reaction to special circumstances. Semaphores are used to synchronize access to shared limited resources. The use of pipes, FIFOs, stream pipes, and message queues, used to exchange data, is very similar. Example programs for all facilities demonstrate the use of these IPC facilities. Some IPC facilities have built-in limits, some of them are stated.


next up previous contents
Next: 4 Networking Principles Up: Unix Communication Facilities Previous: 2 Analysis & Design

Gerhard Müller