🧩 一、进程间通信(IPC)的常见方式
| 通信方式 | 特点 | 是否跨主机 | 典型使用场景 |
|---|---|---|---|
| 1️⃣ 管道(Pipe / FIFO) | 简单、高效,单向通信 | 否 | 父子进程通信 |
| 2️⃣ 命名管道(Named Pipe / FIFO) | 可用于无亲缘关系的进程通信 | 否 | 同一主机不同进程 |
| 3️⃣ 消息队列(Message Queue) | 有结构化消息、可缓存 | 否 | 多进程消息交换 |
| 4️⃣ 共享内存(Shared Memory) | 最高效,直接内存共享 | 否 | 大量数据交换 |
| 5️⃣ 信号量(Semaphore) | 用于同步(非传数据) | 否 | 控制资源访问 |
| 6️⃣ 信号(Signal) | 事件通知机制 | 否 | 异步事件处理 |
| 7️⃣ 套接字(Socket) | 可跨主机、支持 TCP/UDP | ✅ 是 | 分布式或网络通信 |
| 8️⃣ 内存映射文件(mmap) | 文件与内存共享 | 否 | 大文件共享、日志处理 |
🧠 二、各方式简要说明与示例
1. 无名管道(pipe)
-
特点:单向通信,只能在父子进程或兄弟进程间使用。
int fd[2];
pipe(fd);
if (fork() == 0) {
close(fd[0]);
write(fd[1], "hello", 5);
} else {
close(fd[1]);
char buf[10];
read(fd[0], buf, 10);
printf("%s\n", buf);
}
2. 命名管道(FIFO)
允许无亲缘关系进程通信。
mkfifo("/tmp/myfifo", 0666);
3. 消息队列(Message Queue)
-
系统维护一组消息结构体队列,每个消息有类型和内容。
-
优点:结构化数据、异步通信。
int msgid = msgget(1234, IPC_CREAT | 0666);
msgsnd(msgid, &msg, sizeof(msg), 0);
msgrcv(msgid, &msg, sizeof(msg), 0, 0);
4. 共享内存(Shared Memory)
-
最快的通信方式(直接共享内存段)。
-
通常与信号量搭配,用于同步。
int shmid = shmget(1234, 1024, IPC_CREAT | 0666);
char* p = (char*)shmat(shmid, nullptr, 0);
strcpy(p, "hello shared memory");
shmdt(p);
5. 信号量(Semaphore)
-
用于进程同步,不直接传递数据。
sem_t sem;
sem_init(&sem, 0, 1);
sem_wait(&sem);
// 临界区
sem_post(&sem);
6. 信号(Signal)
-
一种异步通知机制,常用于异常或事件处理。
signal(SIGINT, handler);
7. Socket 套接字
-
最灵活,可用于本地(Unix Domain)或远程(TCP/UDP)。
-
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
bind(sockfd, ...);
listen(sockfd, 5);
accept(sockfd, ...);8. 内存映射文件(mmap)
-
多个进程可映射同一文件,实现共享。
int fd = open("data.bin", O_RDWR);
void* p = mmap(nullptr, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
strcpy((char*)p, "hello mmap");
munmap(p, 4096);
⚙️ 三、实际开发中常见组合
| 通信模式 | 常见组合 |
|---|---|
| 大量数据 + 高效率 | 共享内存 + 信号量 |
| 简单控制/命令 | 消息队列 |
| 跨主机通信 | Socket(TCP/UDP) |
| 异步事件响应 | 信号 + 消息队列 |
| 日志共享/文件读写 | mmap |
四、总结(面试答题简述版)
常见的进程间通信方式有:
管道(匿名管道、命名管道)、消息队列、共享内存、信号量、信号、Socket、内存映射文件等。
其中:
管道简单但只能用于亲缘进程;
消息队列支持结构化消息;
共享内存效率最高;
信号量用于同步;
Socket 可跨主机通信
🧩 一、基础题
1️⃣ 进程间通信有哪些方式?区别是什么?
答:
常见方式:
-
管道(Pipe/FIFO):简单,单向通信,适合父子进程。
-
消息队列(Message Queue):支持结构化消息传递。
-
共享内存(Shared Memory):速度最快,直接内存映射。
-
信号量(Semaphore):用于同步和互斥,不传数据。
-
信号(Signal):异步事件通知。
-
Socket(套接字):支持跨主机通信。
-
内存映射文件(mmap):文件与内存共享。
✅ 总结口诀:
“管消息,共信号,套映射”
即:管道、消息队列、共享内存、信号量、信号、套接字、mmap
2️⃣ 管道通信原理是什么?为什么只能用于父子进程?
原理:
-
内核中建立一个缓冲区(环形队列)。
-
写端进程写入数据,读端进程从缓冲区读取。
-
通过
pipe()创建匿名管道后,文件描述符继承给子进程,所以它们能共享同一管道。
原因:
-
只能父子间使用,是因为匿名管道文件描述符通过
fork()继承。
加分点:
若需无亲缘关系通信 → 用 命名管道(FIFO)。
3️⃣ 消息队列与管道的区别?
| 对比项 | 管道 | 消息队列 |
|---|---|---|
| 结构 | 字节流 | 消息体(结构化) |
| 通信方式 | 阻塞式 | 可异步 |
| 持久性 | 随进程消失 | 存在于内核,进程退出后仍存在 |
| 通信对象 | 同一主机 | 同一主机 |
| 效率 | 较低 | 较高(但有拷贝) |
总结:
管道适合简单数据流;消息队列适合命令式、结构化通信。
4️⃣ 共享内存为什么最快?有什么问题?
优点:
-
不需要内核中拷贝数据(零拷贝)。
-
多进程直接访问同一物理内存区域。
缺点:
-
需要同步机制(如信号量)防止并发冲突。
int shmid = shmget(1234, 1024, IPC_CREAT | 0666);
char* p = (char*)shmat(shmid, nullptr, 0);
strcpy(p, "Hello Shared Memory");
shmdt(p);
信号量(Semaphore)与互斥锁(Mutex)区别?
| 特性 | 信号量 | 互斥锁 |
|---|---|---|
| 作用 | 同步或互斥 | 互斥 |
| 计数 | 计数型(可>1) | 仅0或1 |
| 用途 | 控制资源数量 | 控制访问单一资源 |
| 使用范围 | 可跨进程 | 多数仅限线程 |
补充:
-
POSIX 提供
sem_init(),System V 提供semget()。 -
可与共享内存配合使用
6️⃣ 信号(Signal)机制怎么实现的?
信号本质:
-
操作系统发送一个中断事件(如 SIGINT、SIGTERM),
由内核中断当前执行流,调用用户注册的信号处理函数。
void handler(int signo) {
printf("Caught signal %d\n", signo);
}
int main() {
signal(SIGINT, handler);
while(1);
}
Socket 是怎么实现本地和远程通信的?
-
通过
socket()创建文件描述符; -
本地通信使用
AF_UNIX; -
网络通信使用
AF_INET; -
数据通过内核缓冲区在协议栈中传递。
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
bind(sockfd, ...);
listen(sockfd, 5);
accept(sockfd, ...);
跨主机通信 → 使用 TCP/IP(AF_INET)
8️⃣ 内存映射文件(mmap)有什么作用?
-
将文件内容直接映射到内存,可供多个进程共享。
-
高效处理大文件、日志或缓存。
int fd = open("file.txt", O_RDWR);
char* p = (char*)mmap(nullptr, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(p, "hello mmap");
munmap(p, 4096);
🧠 二、进阶题
9️⃣ 在项目中如何选择 IPC 方式?
| 需求 | 推荐方式 |
|---|---|
| 父子进程 | 匿名管道 |
| 无亲缘关系进程 | FIFO、消息队列 |
| 大量数据 | 共享内存 + 信号量 |
| 事件通知 | 信号 |
| 网络通信 | Socket |
| 文件/日志共享 | mmap |
🔟 实际项目中进程同步如何实现?
-
互斥访问资源:信号量 / 互斥锁
-
顺序执行:条件变量
-
通知机制:信号 + 消息队列
-
数据一致性:共享内存 + 信号量保护
| 方式 | 是否双向 | 是否跨主机 | 效率 | 同步需求 | 场景 |
|---|---|---|---|---|---|
| Pipe | ❌ | ❌ | 中 | 无 | 父子进程 |
| FIFO | ✅ | ❌ | 中 | 无 | 无亲缘进程 |
| MsgQueue | ✅ | ❌ | 中 | 无 | 命令传递 |
| Shm + Sem | ✅ | ❌ | 🚀高 | ✅有 | 大数据通信 |
| Signal | ❌ | ❌ | 高 | 无 | 异步通知 |
| Socket | ✅ | ✅ | 中 | 可选 | 网络通信 |
| mmap | ✅ | ❌ | 🚀高 | ✅有 | 文件共享 |
⚙️ 四、提升
面试官问:“你用过哪些进程间通信方式?”
“我主要用过共享内存、消息队列和 Socket。
在性能要求高的项目中,我们使用共享内存配合信号量进行进程同步,避免频繁拷贝;
而在跨主机通信时,我们采用基于 TCP Socket 的通信框架。
对于控制类信号,我会用消息队列或信号通知机制。”
🧩 一、Windows 常见进程间通信方式
| 方式 | 是否跨主机 | 通信特性 | 常用API/类 |
|---|---|---|---|
| 1️⃣ 匿名管道(Anonymous Pipe) | 否 | 父子进程通信 | CreatePipe, ReadFile, WriteFile |
| 2️⃣ 命名管道(Named Pipe) | ✅ 支持本地/远程 | 双向、可靠、流或消息模式 | CreateNamedPipe, ConnectNamedPipe |
| 3️⃣ 邮槽(Mailslot) | ✅ | 单向广播、轻量通信 | CreateMailslot, WriteFile |
| 4️⃣ 内存映射文件(File Mapping) | 否(同机) | 高效共享内存 | CreateFileMapping, MapViewOfFile |
| 5️⃣ 信号量(Semaphore) | 否 | 同步、互斥 | CreateSemaphore, WaitForSingleObject |
| 6️⃣ 事件对象(Event) | 否 | 通知、同步 | CreateEvent, SetEvent, WaitForSingleObject |
| 7️⃣ 互斥量(Mutex) | 否 | 防止多进程同时访问资源 | CreateMutex |
| 8️⃣ 消息队列(WM_COPYDATA / COM / DDE) | 否 | Windows 消息系统 | SendMessage(WM_COPYDATA) |
| 9️⃣ Socket(Winsock) | ✅ | 跨主机通信 | socket, bind, connect, send, recv |
1️⃣ 匿名管道(Anonymous Pipe)
-
只能用于父子进程通信;
-
用
CreatePipe创建读写句柄; -
子进程通过继承句柄通信。
#include <windows.h>
#include <iostream>
int main() {
HANDLE hRead, hWrite;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
CreatePipe(&hRead, &hWrite, &sa, 0);
// 写入数据
const char* msg = "Hello from parent";
DWORD written;
WriteFile(hWrite, msg, strlen(msg), &written, NULL);
// 读取数据
char buf[128];
DWORD read;
ReadFile(hRead, buf, sizeof(buf), &read, NULL);
buf[read] = '\0';
std::cout << "Read: " << buf << std::endl;
CloseHandle(hRead);
CloseHandle(hWrite);
return 0;
}
2️⃣ 命名管道(Named Pipe)
-
支持双向通信;
-
可用于本地或远程主机;
-
一端为服务器,一端为客户端。
服务器端:
HANDLE hPipe = CreateNamedPipe(
L"\\\\.\\pipe\\MyPipe",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, 1024, 1024, 0, NULL);
ConnectNamedPipe(hPipe, NULL);
char buf[128];
DWORD bytesRead;
ReadFile(hPipe, buf, sizeof(buf), &bytesRead, NULL);
buf[bytesRead] = '\0';
printf("Server received: %s\n", buf);
const char* reply = "Hello from server";
DWORD written;
WriteFile(hPipe, reply, strlen(reply), &written, NULL);
CloseHandle(hPipe);
🧩 Windows 命名管道(Named Pipe)完整示例
✅ 服务端:NamedPipeServer.cpp
#include <windows.h>
#include <iostream>
#include <string>
int main() {
// 创建命名管道
HANDLE hPipe = CreateNamedPipe(
L"\\\\.\\pipe\\MyPipe", // 管道名
PIPE_ACCESS_DUPLEX, // 双向通信
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, // 最大实例数
1024, // 输出缓冲区大小
1024, // 输入缓冲区大小
0, // 默认超时
NULL); // 安全属性
if (hPipe == INVALID_HANDLE_VALUE) {
std::cerr << "CreateNamedPipe failed, error = " << GetLastError() << std::endl;
return 1;
}
std::wcout << L"等待客户端连接..." << std::endl;
BOOL connected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (!connected) {
std::cerr << "ConnectNamedPipe failed, error = " << GetLastError() << std::endl;
CloseHandle(hPipe);
return 1;
}
std::cout << "客户端已连接。" << std::endl;
// 接收客户端消息
char buffer[128];
DWORD bytesRead;
BOOL result = ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL);
if (result && bytesRead > 0) {
buffer[bytesRead] = '\0';
std::cout << "收到客户端消息: " << buffer << std::endl;
} else {
std::cerr << "ReadFile failed, error = " << GetLastError() << std::endl;
}
// 回复客户端
const char* reply = "Hello from Named Pipe Server!";
DWORD bytesWritten;
WriteFile(hPipe, reply, (DWORD)strlen(reply), &bytesWritten, NULL);
std::cout << "已回复客户端消息。" << std::endl;
// 关闭管道
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
std::cout << "服务端结束。" << std::endl;
return 0;
}
✅ 客户端:NamedPipeClient.cpp
#include <windows.h>
#include <iostream>
#include <string>
int main() {
// 等待管道可用
if (!WaitNamedPipe(L"\\\\.\\pipe\\MyPipe", NMPWAIT_WAIT_FOREVER)) {
std::cerr << "WaitNamedPipe failed, error = " << GetLastError() << std::endl;
return 1;
}
// 打开命名管道
HANDLE hPipe = CreateFile(
L"\\\\.\\pipe\\MyPipe", // 管道名
GENERIC_READ | GENERIC_WRITE, // 读写模式
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 已存在
0, // 默认属性
NULL); // 模板文件
if (hPipe == INVALID_HANDLE_VALUE) {
std::cerr << "CreateFile failed, error = " << GetLastError() << std::endl;
return 1;
}
std::cout << "成功连接到服务器。" << std::endl;
// 发送消息
std::string message = "Hello from Named Pipe Client!";
DWORD bytesWritten;
BOOL result = WriteFile(hPipe, message.c_str(), (DWORD)message.size(), &bytesWritten, NULL);
if (!result) {
std::cerr << "WriteFile failed, error = " << GetLastError() << std::endl;
} else {
std::cout << "已发送消息到服务器。" << std::endl;
}
// 读取服务器回复
char buffer[128];
DWORD bytesRead;
result = ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL);
if (result && bytesRead > 0) {
buffer[bytesRead] = '\0';
std::cout << "收到服务器回复: " << buffer << std::endl;
} else {
std::cerr << "ReadFile failed, error = " << GetLastError() << std::endl;
}
CloseHandle(hPipe);
std::cout << "客户端结束。" << std::endl;
return 0;
}
面试要点总结
| 特性 | 命名管道(Windows) |
|---|---|
| 通信方向 | 双向 |
| 通信范围 | 本地或远程 |
| 是否面向连接 | 是(需 ConnectNamedPipe) |
| 是否支持多实例 | 是(可设定实例数) |
| 优点 | 简单、安全、可靠 |
| 缺点 | 仅限 Windows 平台 |
成功连接到服务器。
已发送消息到服务器。
收到服务器回复: Hello from Named Pipe Server!
客户端结束。
1万+

被折叠的 条评论
为什么被折叠?



