Inter-Process Communication

24 Jun 2019

Communication between processes

There are some different ways that inter-process communications are done.

  • Signals/Interrupts
    • Notifying that an event has occured.
  • Message Passing
    • Pipes
    • Sockets
  • Shared Memory
    • Subject to race conditions.
    • Synchronization
  • Remote Procedure Calls

Why use inter-process communication (IPC)?

  • Information sharing
    • Shared file that can be accessed concurrently.
  • Computation speedup through sub-tasking
    • Parallel execution of subtasks -> merge the sub-results.
  • Modularity
    • Modular design fashion.
  • Convenient for users
    • Individual tasks to be running in parallel.

Linux Signals and Interrupts

If a process catches a signal, it means that it includes code that will take appropriate action when the signal is received. If the signal is not caught by the process, the kernel will take default action for the signal.

Signals

  • Kernel-to-Process:
    • Kernel sets the numerical code in a process variable, then wakes the process up to handle the signal.
  • Process-to-Process:
    • Can kill(process_id, signal_num)
      • e.g. `kill(Y,SIGUSR1) sends a SIGUSR1 signal to process Y, which will know how to interpret this signal.
      • The call still goes through the kernel, not directly from process to process.
    • A process can send a signal to itself using a library call like alarm()

Blocking vs. Non-Blocking I/O

  • Blocking system call
    • Process is put on wait queue until I/O read or write completes.
    • I/O command succeeds completely or it fails.
  • Non-Blocking system call
    • A write or read returns immediately with partial number of bytes transferred (possibly zero), e.g. keyboard, mouse, network sockets.
    • Makes the application more complex.
      • Not all the data may have been read or written in a signle call.
      • Need to add additional code to handle this, like a loop.

Synchronous vs. Asynchronous

  • Synchronous will make the request I/O and not continue until the command is completed.
    • Often synchronous and blocking are used interchangeably.
  • Asynchronous returns immediately (like non-blocking).
    • Often asynchronous and non-blocking are used interchangeably.
    • In asynchronous write O/O, at some later time, the full number of bytes requested is transferred.
    • Can be implemented using signals and handlers.

Signals and Race Conditions

Signals are an asynchronous signaling mechanism. A process never knows when a signal will occur and its execution can be interrupted at any time. A process must be written to handle asynchrony, otherwise we could encounter race conditions.

int global = 10;
handler(int signum) {
    global++;
}
main() {
    signal(SIGUSR1, handler);
    while(1) {global--;}
}

Multiple signals can have a race condition. For example, let’s say a signal handler is processing signal S1, but is interrupted by another signal S2.

The solution is to block other signals while handling the current signal. * Use sigprocmask() to selectively block other signals. * A blocked signal is pending. * There can be at most one pending signal per signal type, so signals are not queued.

#include <signal.h>
int beeps = 0;

void handler(int sig) {
    if (beeps < 5) {
        alarm(3);
        beeps++;
    } else {
        printf("DONE\n");
        exit(0);
    }
}

int main() {
    signal (SIGALRM, handler); // Register signal handler
    alarm(3); // Cause the first SIGALRM to be sent to this process in 3 secs
    while(1) { ; } // Infinite loop that gets interrupted by signal handling
    exit(0);
}

IPC Message Passing

  • send() and receive() can be blocking/synchronous or non-blocking/asynchronous.
  • Used to pass small messages.
  • Advantage: the OS handles synchronization.
  • Disadvantage: it’s slow.
    • The OS is involved in each IPC operation for control signaling and possibly data as well.
  • Message Passing IPC types:
    • Pipes
    • UNIX-domain sockets
    • internet domain sockets
    • message queues
    • remote procedure calls (RPC)

IPC via Pipes

Named Pipes

Traditional one-way or anonymous pipes only exist transiently between the two processes connected by the pipe. As soon as these processes complete, the pipe disappears.

Named pipes persist across processes

  • They operate as FIFO buffers or files, e.g. created using mkfifo(unique_pip_name) on Unix.
  • Different processes can attach to the named pipe to send and receive data.
  • Need to explicitly remove the named pipe.

Remote Procedure Call (RPC)

  • Can fail more often.
  • Can be executed more than once.
    • Exactly once vs. at most once.
    • Timestamped message record is needed for “at most once”.
    • An ACK protocol is needed for “exactly once”.
  • Remote port selection
    • Static port -> pre-selected.
    • Dynamic port -> matchmaker is needed.
  1. Client makes a call to a function and passes parameters.
  2. The client has linked a stub for the function. This stub will marshal (packetize) the data and send it to a remote server.
  3. The network transfers the information to a server.
  4. A service listening to the network receives a request.
  5. The information is unmarshalled (unpacked) and the server’s function is called.
  6. The results are returned to be marshalled into a packet.
  7. The network transfers the packet back to the client.
  8. TCP/IP used to transmit packet.

IPC Shared Memory

The OS provides mechanisms for the creation of a shared memory buffer between processes (both processes have address mapped to their process). It applies to processes on the same machine. There is a problem that comes with this, which is that shared access introduces complexity - we need to synchronize access.

shmid - shmget(key_name, size, flags) is part of the POSIX API. It creates a shared memory segment using a name (key ID).

All processes sharing the memory need to agree on the key name in advance.

This creates a new shared memory segment if no such shared memory with the same name exists and returns a handle to the shared memory. If it already exists, then it just returns the handle.

shm_ptr = shmat(shmid, NULL, 0) attaches a shared memory segment to a process’s address space. This association is also called binding and reads/writes now just use shm_ptr.

Use shmctl() to modify control information and permissions related to a shared memory segment, and to remove a shared memory segment.

Top