Interprocess communication is defined as follows on Wikipedia:

In computing, inter-process communication (IPC) is a set of methods for the exchange of data among multiple threads in one or more processes. Processes may be running on one or more computers connected by a network.

More or less it is exactly what you would expect, the transfer of data between processes. This may sound straight forward, but I assure you that is not the case. The code throughout this post was taken from or inspired by various sources, most notably the Systems Programming course at the University of Illinois.

Use Cases

There are several good reasons and common use cases for IPC:

Sharing information/data – Share data between processes to synchronize different applications

– Share data between processes to synchronize different applications Computational Speedups – Sending data off site for processing

– Sending data off site for processing Modularity – Google Chrome separates each tab into a separate process to help avoid crashes, as well as security.

– Google Chrome separates each tab into a separate process to help avoid crashes, as well as security. Development – Often it is easier to have two teams build separate programs. By building them with IPC in mind you can essentially “plugin” to any programs which need similar functionality without any recompiling

– Often it is easier to have two teams build separate programs. By building them with IPC in mind you can essentially “plugin” to any programs which need similar functionality without any recompiling Security – It is often useful to separate processes to ensure system security. Each process has it’s own memory and if they communicate as opposed to sharing memory the process will be modularized and possibly more secure.

The Types of IPC

Shared Memory – Processes share a single or multiple segments of physical memory that both processes can read and at least one can write to.

– Processes share a single or multiple segments of physical memory that both processes can read and at least one can write to. Mapped Memory – Similar to shared memory, except it is associated with a file on the system, which is used for communication.

– Similar to shared memory, except it is associated with a file on the system, which is used for communication. Pipes – Directed information flow from one process to another.

– Directed information flow from one process to another. FIFOs (Named Pipes) – Similar to pipes, except that unrelated processes can communicate because the pipe is given a name in the filesystem. FIFOs are comparable to the mapped memory vs shared memory version of a pipe.

– Similar to pipes, except that unrelated processes can communicate because the pipe is given a name in the filesystem. FIFOs are comparable to the mapped memory vs shared memory version of a pipe. Sockets – Networked information transfer, allows IPC from processes on different computers.

Shared Memory

Shared memory IPCs refer to sharing a physical memory location where multiple processes read and write to. The processes do this by mapping local memory to the shared physical memory location (via pointers or some other method). The physical memory is then used throughout the processes life spawn, meaning some processes can finish and close, but the physical memory remains until it is released (and all processes detach). Below is a representation of how shared memory functions:

In the picture above, if processes 2 and 3 finished running the shared memory would continue to exist in shared memory. If then another processes (call it process 4) started running it could also gain accesses to the shared memory if the process was of the same type (meaning if it had permission). This method of IPC does not provide mutual exclusion and processes must handle this in some manner. It is possible (and advised) to address the issue of mutual exclusion by building a mutex that spans processes. Programers must recognize this and program accordingly.

An abridged programming example (check github for full version):

View the code on Gist.

Output:

$ ./a.out

Read Message Contains: “”

$ ./a.out “this is a test”

Writing Message: “this is a test”

$ ./a.out

Read Message Contains: “this is a test”

As you can see the message is actually stored/saved between the processes being executed.

Mapped Memory

The mapped memory IPC is much like the shared memory. There are however some key differences between shared memory and mapped memory are that mapped memory. The way mapping memory works is you map a disk block to a page in memory, you can then use memory accesses as usual (with pointers), but in actuality you are writing/reading out to disk. Meaning even if you restart the machine or the machine crashes the file will still be present.



Mapping gives the programmer several advantages, on the one hand you are storing a file and usual I/O requires a significant amount of time to read/write, but with mapping you can simply change the array and this is handled for you. It is also randomly accessible as opposed to using seek (in C).

An abridged programming example (check github for full version):

View the code on Gist.

I recommend going to github, downloading the code and testing it out. It’s pretty easy to do, and the directions are in the program comments. Essentially, you start one terminal and run ./a.out <filename to read/write from> read, then open another terminal as ./a.out <filename> write. You can then type stuff on the write side and when you type <enter> you should see it on the read terminal.

Pipes

Pipes are relatively straight forward. Instead of using shared/mapped memory to share data among processes, instead we “pipe” data across processes with the help of the operating system. Although this does not mean that less memory is used necessarily (by the system), but it does remove the programmers requirement to manage it. In C programming pipes are extremely easy to use and is used more often than than shared memory (and perhaps mapped memory IPC). The major advantage they have over the shared or mapped memory IPCs is that the programmer does not have to worry about does not have to worry about mutual exclusion for read/writing, the operating system handles it all.

As you can see from the picture, compared to the shared/mapped memory there is not an intermediate step which the programmer controls. Rather, the operating system is utilized in pipes to transfer data from one process to the other. This removes both the control of the programmer (which is often useful), but also greatly simplifies the IPC (and reduces mistakes). Note, the pipe can go one direction i.e. you can go from process 1 to 2 or 2 to 1, but you need another pipe to do both. By default all pipes are unnamed, what this means is that the pipe is removed when no process is using it (by the system).

An abridged programming example (check github for full version):

View the code on Gist.

Output

$ ./a.out

PARENT: reading from pipe

CHILD: writing to pipe

CHILD: exiting

PARENT: read “This is a test”

FIFOs (Named Pipes)

FIFOs are also called “named pipes,” these are different than the standard pipes because they “named” or persistent even if no process is currently using the FIFO. This seems to be very similar to the shared memory vs mapped memory example, in that FIFOs are (or at least appear) to be saved to the file system similar to mapped memory. In C there are several very easy commands to use a FIFO and it is easier to use than pipes since we do not have multiple pipe descriptors.

Notice it is the exact same picture as the pipe, that is because it carries out essentially the same function. The difference is that the FIFO exists until it is destroyed, but the operating system handles the saving and what not for you. Remember synchronization is taken care of by the operating system for FIFOs as well as pipes!

An abridged programming example (check github for full version):

View the code on Gist.

Output

$ ./a.out fifo.c

Reading File fifo.c

…… abridged ….

26| mkfifo(argv[1], S_IRWXU | S_IRWXG | S_IRWXO);

27| int fifo = open(argv[1], O_RDONLY);

28|

29| /* Duplicate the file ‘fifo’, so that file descriptor 0 points to it.

30| * Note that 0 is the file descriptor of stdin. */

31| dup2(fifo, 0);

32|

33| char line[1024];

34| int i = 0;

35|

36| printf(“Reading File %s

”, argv[1]);

37| while(fgets(line, 1024, stdin))

38| printf(“%3d| %s”, i++, line);

39| printf(“

”);

40| }

Sockets

Sockets work very similar to pipes (in theory), but require a fair amount more information and are more complex. That being said, this introduction will describe it best with the picture below:

As you can see each process is on a separate computer, the data then travels through the network to another computer and to the desired process. As a redditor pointed out, you can also use sockets locally by using the “localhost” to essentially loop-back to the another process on the system. If you wish to see an example, you can visit my post Intro to IPC | Sockets.

Related Articles