Skip to Content
Course content

150: Inter-Process Communication Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've spent a lot of time managing memory and threads within a single process. But in the real world, monolithic programs are often a liability. If your telemetry module crashes, you don't want it taking down your entire engine controller. That's where Inter-Process Communication (IPC) comes in. It allows us to split our logic into separate OS processes that can still talk to each other.

There are a dozen ways to do this—sockets, shared memory, message queues—but for today, I want to show you Named Pipes (FIFOs). They're essentially files that live on the disk but behave like a queue in memory. I like them for basic tools because they're simple to debug; you can literally cat a pipe from your terminal to see what your program is sending.

Designing a Simple Event Logger

Let's build a basic system where one process (the Producer) sends system events, and another process (the Consumer) reads them and logs them to the console. I'm thinking of this as a "fire and forget" system. The producer shouldn't care if the logger is currently busy or even running.

First, we need to create the pipe. In C++, we use mkfifo from the header. Here is how I'll set up the producer:

#include <iostream>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>

int main() {
    const char* pipePath = "/tmp/my_event_pipe";
    mkfifo(pipePath, 0666); // Create the named pipe

    int fd = open(pipePath, O_WRONLY); 
    std::string event = "System Startup: OK";
    write(fd, event.c_str(), event.length());
    
    close(fd);
    return 0;
}

The "Hanging" Problem

Now, here is where I usually trip up the first few times I do this. I wrote the code above, compiled it, and ran it. The program just... stopped. It didn't crash, it didn't print anything; it just sat there. I initially thought I had a deadlock or a permissions issue with /tmp.

After a quick check of the man pages, I remembered a crucial detail about FIFOs: opening a pipe for writing will block the process until another process opens that same pipe for reading. The OS is essentially forcing the two processes to synchronize their handshake. My producer was waiting for a consumer that didn't exist yet.

To fix this during development, I have two choices: I can start the reader first, or I can open the pipe with the O_NONBLOCK flag. For this example, we'll stick to the standard blocking behavior because it ensures no data is lost before the listener is ready.

Creating the Event Listener

Now let's write the consumer. This one is simpler: it opens the pipe for reading, waits for data, and prints it. I'll wrap it in a loop so it stays alive and processes multiple events.

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <vector>

int main() {
    const char* pipePath = "/tmp/my_event_pipe";
    
    // Open for reading; this will block until the producer opens for writing
    int fd = open(pipePath, O_RDONLY);
    
    char buffer[128];
    while (true) {
        ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
        if (bytesRead < 0) break; 
        if (bytesRead == 0) {
            // Producer closed the connection; let's wait a bit or exit
            break; 
        }
        
        buffer[bytesRead] = '\0'; // Null-terminate the string
        std::cout < "[LOG]: " < buffer < std::endl;
    }

    close(fd);
    return 0;
}

Connecting the Two Pieces

To see this in action, you can't just run one program. You need two separate terminal windows. In the first window, run the consumer. It will look like it's doing nothing—that's the open() call blocking. Then, in the second window, run the producer.

The moment the producer executes open(), the kernel wakes up the consumer. The data flows through the pipe, the consumer prints the message, and both processes finish their work. It's a primitive form of communication, but it's the foundation for how much of the Unix ecosystem works under the hood.




📋 Practical Task

Build a Bi-Directional Heartbeat Monitor

In the lesson, we built a one-way communication channel. Now, I want you to implement a "Heartbeat" system between two processes. This requires two separate named pipes: one for the "Ping" and one for the "Pong".

  • Process A (The Monitor): Sends a string "PING" through pipe_a, then immediately waits to read a response from pipe_b. If it receives "PONG", it prints "Heartbeat healthy".
  • Process B (The Worker): Waits to read from pipe_a. As soon as it sees "PING", it writes "PONG" back into pipe_b.

Your goal is to make this loop 5 times. Pay close attention to the order in which you open the pipes in each process to avoid a "Deadlock" where both processes are blocking on an open() call and neither can proceed.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.