在 Linux 系统中,进程间通信(Inter-Process Communication, IPC)是实现不同进程间数据交换和同步的核心机制。本文将通过完整的客户端 - 服务器代码示例,详细解析每种 IPC 机制的实现原理和使用方法。
一、管道(Pipe)
介绍
管道是一种半双工的通信方式,数据只能在一个方向上流动,且只能在具有亲缘关系的进程间使用(如父子进程)。它基于内存缓冲区实现,是 Unix/Linux 系统中最基本的 IPC 机制。
使用场景
- 命令行中的管道操作(如
ls | grep .txt
) - 父子进程间的简单数据传输
- 数据流处理(如生产者 - 消费者模型)
注意事项
- 单向性:数据只能从写端流向读端,若需双向通信需创建两个管道
- 阻塞特性:读 / 写操作可能阻塞,直到有数据可读 / 写空间可用
- 亲缘关系:只能用于具有共同祖先的进程
示例代码
// pipe_example.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define BUFFER_SIZE 1024
int main() {
int fd[2];
pid_t pid;
char buffer[BUFFER_SIZE];
// 创建管道
if (pipe(fd) == -1) {
perror("pipe creation failed");
return 1;
}
// 创建子进程
pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid > 0) { // 父进程 (服务器)
close(fd[1]); // 关闭写端
// 从管道读取数据
ssize_t bytes_read = read(fd[0], buffer, BUFFER_SIZE);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Server received: %s\n", buffer);
}
close(fd[0]); // 关闭读端
} else { // 子进程 (客户端)
close(fd[0]); // 关闭读端
const char *message = "Hello from client!";
write(fd[1], message, strlen(message)); // 向管道写入数据
close(fd[1]); // 关闭写端
}
return 0;
}
代码解析
- 管道创建:
pipe(fd)
创建一对文件描述符,fd[0]
用于读,fd[1]
用于写 - 父子进程通信:
- 父进程关闭写端 (
fd[1]
),通过read()
接收数据 - 子进程关闭读端 (
fd[0]
),通过write()
发送数据
- 父进程关闭写端 (
- 资源管理:通信结束后需关闭所有文件描述符,管道随进程终止自动销毁
二、命名管道(FIFO)
介绍
命名管道(FIFO)是一种特殊类型的文件,它突破了普通管道的亲缘关系限制,允许无关进程间通信。FIFO 基于文件系统路径名来标识,数据在内存中缓存,但通过文件系统接口访问。
使用场景
- 无关进程间的数据流通信
- 客户端 - 服务器架构(如 Web 服务器与 CGI 脚本)
- 系统日志服务接收多个进程的日志消息
注意事项
- 文件依赖性:需手动创建和删除 FIFO 文件
- 同步问题:打开操作可能阻塞,直到有对应读 / 写操作
- 权限控制:需设置合适的文件权限(如
0666
)
示例代码
// fifo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define FIFO_NAME "myfifo"
#define BUFFER_SIZE 1024
int main() {
char buffer[BUFFER_SIZE];
int fd;
// 创建命名管道(若不存在)
if (mkfifo(FIFO_NAME, 0666) == -1) {
perror("mkfifo failed");
if (errno != EEXIST) return 1;
}
printf("Server waiting for client...\n");
// 打开管道读取数据(阻塞直到有写者)
fd = open(FIFO_NAME, O_RDONLY);
if (fd == -1) {
perror("open failed");
return 1;
}
// 读取数据
ssize_t bytes_read = read(fd, buffer, BUFFER_SIZE);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Server received: %s\n", buffer);
}
// 清理资源
close(fd);
unlink(FIFO_NAME); // 删除命名管道
return 0;
}
// fifo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#de