C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
150: Inter-Process Communication Basics
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 frompipe_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 intopipe_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.
There are no comments for now.