In C++, Inter-Process Communication (IPC) is a mechanism that allows multiple independent processes to exchange data and coordinate operations. Below are detailed descriptions of three common IPC methods:
1. Pipes
Pipes are a half-duplex communication method where data can only flow in one direction, and they are divided into anonymous pipes and named pipes.
Anonymous Pipes
-
Characteristics:
-
Can only be used between parent and child processes or sibling processes (related processes).
-
One-way communication, one end reads, and the other end writes.
-
Lifecycle is destroyed when the process ends.
-
Principle:
-
Created through the system call pipe(int fd[2]), which returns two file descriptors: fd[0] (read end) and fd[1] (write end).
-
Data written to the pipe is stored in the kernel buffer and is removed upon reading.
-
Example Code (Parent-Child Process Communication):
#include <unistd.h>
#include <iostream>
int main() {
int fd[2];
char buffer[100];
if (pipe(fd) == -1) {
perror("pipe failed");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid == 0) { // Child process: write data
close(fd[0]); // Close read end
const char* msg = "Hello from child!";
write(fd[1], msg, strlen(msg) + 1);
close(fd[1]);
} else { // Parent process: read data
close(fd[1]); // Close write end
read(fd[0], buffer, sizeof(buffer));
std::cout << "Parent received: " << buffer << std::endl;
close(fd[0]);
}
return 0;
}
Named Pipes (FIFOs)
-
Characteristics:
-
Exist as files (created using mkfifo), can be used by unrelated processes.
-
Follow the first-in, first-out (FIFO) principle.
-
Processes read and write by opening the file.
-
Example Code:
// Writer Process
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("myfifo", O_WRONLY);
write(fd, "Hello from writer!", 18);
close(fd);
return 0;
}
// Reader Process
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
int main() {
int fd = open("myfifo", O_RDONLY);
char buffer[100];
read(fd, buffer, 100);
std::cout << "Reader received: " << buffer << std::endl;
close(fd);
return 0;
}
Note: FIFO file must be created first: mkfifo myfifo.
2. Shared Memory
Shared memory allows multiple processes to directly access the same physical memory, making it the fastest IPC method.
-
Characteristics:
-
Efficient: Data does not need to be copied, accessed directly via pointers.
-
Requires synchronization: Must use semaphores or mutexes to avoid race conditions.
-
Lifecycle: Independent of processes, must be manually deleted.
-
Principle:
-
Create a shared memory segment (shmget).
-
Map it to the process’s address space (shmat).
-
Processes read and write memory, then unmap (shmdt) and delete (shmctl) after operations are complete.
-
Example Code:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <cstring>
int main() {
// Create shared memory segment (key 1234, size 1024 bytes)
int shmid = shmget(1234, 1024, 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget failed");
return 1;
}
// Map to current process
char* shared_memory = (char*)shmat(shmid, nullptr, 0);
if (shared_memory == (char*)-1) {
perror("shmat failed");
return 1;
}
// Write data
std::strcpy(shared_memory, "Hello from shared memory!");
// Unmap
if (shmdt(shared_memory) == -1) {
perror("shmdt failed");
return 1;
}
// Reading process is similar, just change the write operation to read
return 0;
}
3. Message Queues
Message queues are a linked list managed by the kernel, allowing processes to communicate by sending/receiving messages.
-
Characteristics:
-
Asynchronous communication: The sender and receiver do not need to run simultaneously.
-
Message types: Messages can be categorized by type, allowing the receiver to select the type to receive.
-
Persistence: Messages remain until read or the queue is deleted.
-
Principle:
-
Create a message queue (msgget).
-
Send messages (msgsnd) and receive messages (msgrcv).
-
Delete the queue (msgctl).
-
Example Code:
#include <sys/msg.h>
#include <iostream>
#include <cstring>
struct Message {
long mtype; // Message type (must be greater than 0)
char mtext[100]; // Message content
};
int main() {
// Create message queue (key 5678)
int msgid = msgget(5678, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget failed");
return 1;
}
// Send message
Message msg;
msg.mtype = 1;
std::strcpy(msg.mtext, "Hello from message queue!");
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd failed");
return 1;
}
// Receive message
Message received;
if (msgrcv(msgid, &received, sizeof(received.mtext), 1, 0) == -1) {
perror("msgrcv failed");
return 1;
}
std::cout << "Received: " << received.mtext << std::endl;
// Delete queue
if (msgctl(msgid, IPC_RMID, nullptr) == -1) {
perror("msgctl failed");
return 1;
}
return 0;
}
4. Comparison and Selection
|
Method |
Advantages |
Disadvantages |
Applicable Scenarios |
|
Pipes |
Simple to use, automatic synchronization |
One-way, limited capacity |
Small data transfer between parent and child processes |
|
Shared Memory |
Fastest speed, suitable for large data |
Requires manual synchronization, complex management |
High-performance computing, graphics processing |
|
Message Queues |
Asynchronous communication, categorized by type |
Less efficient than shared memory |
Distributed systems, event-driven architectures |
Considerations
-
Synchronization Issues: Shared memory and pipes need to be aware of race conditions; it is recommended to use semaphores or mutexes.
-
Resource Release: Shared memory and message queues need to be manually deleted to avoid memory leaks.
-
Platform Differences: IPC implementations differ between Windows and Linux; the above examples are suitable for POSIX systems.