Inter-Process Communication (IPC) enables different processes to exchange data and coordinate operations within an operating system. Below are common IPC mechanisms along with implementation examples for both Windows and Linux platforms.
Fundamentals
- Pipe: A half-duplex communication channel typical used between related processes like parent-child. Data flows in one direction through a FIFO file descriptor.
- Named Pipe: A special file allowing unrelated processes to communicate via the filesystem. It has a path name and supports cross-process communication.
- Signal: An asynchronous notification mechanism that alerts a process of events such as termination (
SIGINT). Process handlers can be registered usingsignal(). - Message Queue: Allows processes to send and receive messages via a queue. Each message has a type and is identified by a unique key.
- Shared Memory: Provides high-speed data sharing among multiple processes by mapping the same memory region into their address spaces. Requires synchronization to prevent race conditions.
- Semaphore: A counter-based synchronization primitive used to control access to shared resources. Processes perform P (wait) and V (signal) operations.
- Socket: Enables network communication but also supports local inter-process communication. Offers flexible communication over various protocols.
Windows IPC
Named Pipes
Server:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hPipe;
char buffer[1024];
DWORD bytesRead;
hPipe = CreateNamedPipe(
TEXT("\\\\.\\pipe\\MyPipe"),
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, NULL);
ConnectNamedPipe(hPipe, NULL);
ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL);
std::cout << "Received: " << buffer << std::endl;
CloseHandle(hPipe);
return 0;
}
Client:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hPipe;
const char* message = "Hello from client";
DWORD bytesWritten;
hPipe = CreateFile(
TEXT("\\\\.\\pipe\\MyPipe"),
GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
WriteFile(hPipe, message, strlen(message) + 1, &bytesWritten, NULL);
CloseHandle(hPipe);
return 0;
}
Shared Memory
Writer:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, L"MySharedMem");
pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 256);
CopyMemory((PVOID)pBuf, "Data from writer", strlen("Data from writer") + 1);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
Reader:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, L"MySharedMem");
pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 256);
std::cout << "Received: " << pBuf << std::endl;
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
Message Queues
Sender:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hQueue;
const char* message = "Hello from sender";
hQueue = CreateMailslot(TEXT("\\\\.\\mailslot\\MySlot"), 0, MAILSLOT_WAIT_FOREVER, NULL);
WriteFile(hQueue, message, strlen(message) + 1, NULL, NULL);
CloseHandle(hQueue);
return 0;
}
Receiver:
#include <windows.h>
#include <iostream>
int main() {
HANDLE hQueue;
char buffer[1024];
DWORD bytesRead;
hQueue = CreateFile(TEXT("\\\\.\\mailslot\\MySlot"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ReadFile(hQueue, buffer, sizeof(buffer), &bytesRead, NULL);
std::cout << "Received: " << buffer << std::endl;
CloseHandle(hQueue);
return 0;
}
Semaphores
#include <windows.h>
#include <iostream>
int main() {
HANDLE hSem = CreateSemaphore(NULL, 0, 1, L"MySemaphore");
ReleaseSemaphore(hSem, 1, NULL);
std::cout << "Semaphore signaled" << std::endl;
CloseHandle(hSem);
return 0;
}
Sockets
Server:
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
int main() {
WSADATA wsaData;
SOCKET sListen, sClient;
SOCKADDR_IN addr;
char buffer[1024];
WSAStartup(MAKEWORD(2, 2), &wsaData);
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, 5);
sClient = accept(sListen, NULL, NULL);
recv(sClient, buffer, sizeof(buffer), 0);
std::cout << "Received: " << buffer << std::endl;
closesocket(sClient);
closesocket(sListen);
WSACleanup();
return 0;
}
Client:
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
int main() {
WSADATA wsaData;
SOCKET sClient;
SOCKADDR_IN addr;
char* message = "Hello from client";
WSAStartup(MAKEWORD(2, 2), &wsaData);
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8080);
connect(sClient, (SOCKADDR*)&addr, sizeof(addr));
send(sClient, message, strlen(message), 0);
closesocket(sClient);
WSACleanup();
return 0;
}
Linux IPC
Pipes
Parent:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int pipefd[2];
pid_t pid;
char buffer[1024];
pipe(pipefd);
pid = fork();
if (pid == 0) { // Child
close(pipefd[1]);
read(pipefd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);
close(pipefd[0]);
exit(0);
} else { // Parent
close(pipefd[0]);
write(pipefd[1], "Hello from parent", 18);
close(pipefd[1]);
wait(NULL);
exit(0);
}
}
Shared Memory
Writer:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int shmid;
char* shmaddr;
key_t key = 1234;
shmid = shmget(key, 1024, IPC_CREAT | 0666);
shmaddr = shmat(shmid, NULL, 0);
strcpy(shmaddr, "Data from writer");
shmdt(shmaddr);
return 0;
}
Reader:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int shmid;
char* shmaddr;
key_t key = 1234;
shmid = shmget(key, 1024, 0666);
shmaddr = shmat(shmid, NULL, 0);
printf("Received: %s\n", shmaddr);
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
Message Queues
Sender:
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct msgbuf {
long mtype;
char mtext[1024];
};
int main() {
int msgid;
struct msgbuf msg;
key_t key = 1234;
msgid = msgget(key, IPC_CREAT | 0666);
msg.mtype = 1;
strcpy(msg.mtext, "Hello from sender");
msgsnd(msgid, &msg, sizeof(msg), 0);
return 0;
}
Receiver:
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
struct msgbuf {
long mtype;
char mtext[1024];
};
int main() {
int msgid;
struct msgbuf msg;
key_t key = 1234;
msgid = msgget(key, 0666);
msgrcv(msgid, &msg, sizeof(msg), 1, 0);
printf("Received: %s\n", msg.mtext);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
Semaphores
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int semid;
struct sembuf op;
semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
semctl(semid, 0, SETVAL, 0);
op.sem_num = 0;
op.sem_op = 1;
op.sem_flg = 0;
semop(semid, &op, 1);
printf("Semaphore signaled\n");
semctl(semid, 0, IPC_RMID);
return 0;
}
Sockets
Server:
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int sockfd, newsockfd;
socklen_t clilen;
char buffer[1024];
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(8080);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(sockfd, 5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
read(newsockfd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(newsockfd);
close(sockfd);
return 0;
}
Client:
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main() {
int sockfd;
struct sockaddr_in serv_addr;
char* message = "Hello from client";
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
write(sockfd, message, strlen(message));
close(sockfd);
return 0;
}
Summary
Each IPC mechanism offers distinct advantages depending on use case:
- Pipes: Simple unidirectional communication between related processes.
- Shared Memory: Fastest method for large data exchange requiring synchronization.
- Message Queues: Flexible message passing supporting multiple types.
- Semaphores: Synchronization tool controlling access to shared resources.
- Sockets: Versatile communication over networks or locally with protocol flexibility.