在 Linux 系统中,进程间通信(IPC,Inter-Process Communication)是实现多进程协作的核心机制。常见的 IPC 方法包括:管道(Pipe)、命名管道(FIFO)、信号(Signal)、共享内存(Shared Memory)、消息队列(Message Queue)、信号量(Semaphore)和套接字(Socket)。以下分别介绍这些方法并提供示例代码。
在 Linux 系统中,进程间通信(IPC)是多进程协作的基础,提供了多种机制满足不同场景需求。以下详细介绍七种常用 IPC 方法并提供示例代码:
1. 管道(Pipe)
特点:
- 半双工通信(单向传输),仅适用于父子或兄弟进程
- 基于内存的临时通道,进程退出后自动销毁
- 无需显式创建文件节点
示例:父子进程管道通信
运行
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main() {
int pipefd[2]; // 0:读端, 1:写端
pid_t pid;
char buf[100];
// 创建管道
if (pipe(pipefd) == -1) {
perror("pipe failed");
return 1;
}
pid = fork();
if (pid == -1) {
perror("fork failed");
return 1;
}
if (pid == 0) { // 子进程:读取数据
close(pipefd[1]); // 关闭写端
read(pipefd[0], buf, sizeof(buf));
printf("子进程收到: %s\n", buf);
close(pipefd[0]);
} else { // 父进程:写入数据
close(pipefd[0]); // 关闭读端
const char* msg = "Hello from parent";
write(pipefd[1], msg, strlen(msg)+1);
close(pipefd[1]);
wait(NULL); // 等待子进程结束
}
return 0;
}
编译运行:
bash
gcc pipe_demo.c -o pipe_demo && ./pipe_demo
2. 命名管道(FIFO)
特点:
- 允许无亲缘关系进程通信,通过文件系统路径标识
- 半双工通信,需要显式创建和删除
- 具有文件属性,可通过
ls查看,rm删除
示例:两个独立进程通信
写进程(fifo_write.c):
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main() {
const char* fifo_path = "/tmp/myfifo";
// 创建FIFO
mkfifo(fifo_path, 0666);
int fd = open(fifo_path, O_WRONLY);
const char* msg = "Hello from FIFO writer";
write(fd, msg, strlen(msg)+1);
close(fd);
unlink(fifo_path); // 可选:删除FIFO
return 0;
}
读进程(fifo_read.c):
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
const char* fifo_path = "/tmp/myfifo";
char buf[100];
int fd = open(fifo_path, O_RDONLY);
read(fd, buf, sizeof(buf));
printf("收到: %s\n", buf);
close(fd);
return 0;
}
编译运行:
# 终端1
gcc fifo_read.c -o fifo_read && ./fifo_read
# 终端2
gcc fifo_write.c -o fifo_write && ./fifo_write
3. 信号(Signal)
特点:
- 用于通知进程发生特定事件的异步机制
- 只能传递信号编号,不能携带大量数据
- 每个信号有默认处理方式,可自定义处理函数
示例:信号发送与处理
接收进程(signal_recv.c):
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
// 自定义信号处理函数
void handle_sigusr1(int signo) {
printf("收到SIGUSR1信号\n");
}
int main() {
printf("接收进程PID: %d\n", getpid());
signal(SIGUSR1, handle_sigusr1); // 注册信号处理函数
while(1) pause(); // 等待信号
return 0;
}
发送进程(signal_send.c):
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("用法: %s <目标进程PID>\n", argv[0]);
return 1;
}
pid_t pid = atoi(argv[1]);
kill(pid, SIGUSR1); // 发送信号
printf("已发送SIGUSR1信号\n");
return 0;
}
编译运行:
# 终端1
gcc signal_recv.c -o signal_recv && ./signal_recv
# 终端2 (替换1234为实际PID)
gcc signal_send.c -o signal_send && ./signal_send 1234
4. 共享内存(Shared Memory)
特点:
- 多个进程共享同一块物理内存,IPC 中速度最快
- 需配合同步机制(如信号量)避免数据竞争
- 通过键值标识,需显式创建和销毁
示例:共享内存读写
写进程(shm_write.c):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define SHM_KEY 0x1234
#define SHM_SIZE 1024
int main() {
// 创建共享内存
int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) { perror("shmget"); return 1; }
// 映射到进程地址空间
char* shm_ptr = (char*)shmat(shmid, NULL, 0);
if (shm_ptr == (void*)-1) { perror("shmat"); return 1; }
// 写入数据
strcpy(shm_ptr, "Hello from shared memory");
printf("已写入共享内存\n");
// 等待读取
printf("按回车继续...\n");
getchar();
// 清理
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
return 0;
}
读进程(shm_read.c):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_KEY 0x1234
#define SHM_SIZE 1024
int main() {
// 获取共享内存
int shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
if (shmid == -1) { perror("shmget"); return 1; }
// 映射到进程地址空间
char* shm_ptr = (char*)shmat(shmid, NULL, 0);
if (shm_ptr == (void*)-1) { perror("shmat"); return 1; }
// 读取数据
printf("从共享内存读取: %s\n", shm_ptr);
// 清理
shmdt(shm_ptr);
return 0;
}
编译运行
# 终端1
gcc shm_write.c -o shm_write && ./shm_write
# 终端2
gcc shm_read.c -o shm_read && ./shm_read
5. 消息队列(Message Queue)
特点:
- 以消息为单位传递数据,支持按类型接收
- 消息有优先级,可实现异步通信
- 生命周期随内核,需显式删除
示例:消息队列通信
发送进程(msg_send.c):
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#define MSG_KEY 0x4321
// 消息结构
struct msgbuf {
long mtype; // 消息类型
char mtext[100]; // 消息内容
};
int main() {
// 创建消息队列
int msqid = msgget(MSG_KEY, IPC_CREAT | 0666);
if (msqid == -1) { perror("msgget"); return 1; }
struct msgbuf msg;
msg.mtype = 1; // 设置消息类型
strcpy(msg.mtext, "Hello from message queue");
// 发送消息
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
printf("消息已发送\n");
return 0;
}
接收进程(msg_recv.c)
#include <stdio.h>
#include <sys/msg.h>
#define MSG_KEY 0x4321
struct msgbuf {
long mtype;
char mtext[100];
};
int main() {
// 获取消息队列
int msqid = msgget(MSG_KEY, 0666);
if (msqid == -1) { perror("msgget"); return 1; }
struct msgbuf msg;
// 接收类型为1的消息
msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0);
printf("收到消息: %s\n", msg.mtext);
// 删除消息队列
msgctl(msqid, IPC_RMID, NULL);
return 0;
}
编译运行:
# 终端1
gcc msg_recv.c -o msg_recv && ./msg_recv
# 终端2
gcc msg_send.c -o msg_send && ./msg_send
6. 信号量(Semaphore)
特点:
- 用于进程同步,控制对共享资源的访问
- 通过 PV 操作(P: 获取资源,V: 释放资源)实现
- 可用于多个进程间的同步
示例:信号量同步
#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <unistd.h>
#define SEM_KEY 0x5678
// 信号量操作函数
void sem_op(int semid, int op) {
struct sembuf sops;
sops.sem_num = 0; // 信号量编号
sops.sem_op = op; // 操作:-1(P), +1(V)
sops.sem_flg = 0;
semop(semid, &sops, 1);
}
int main() {
// 创建信号量(初始值1)
int semid = semget(SEM_KEY, 1, IPC_CREAT | 0666);
semctl(semid, 0, SETVAL, 1); // 设置初始值
if (fork() == 0) { // 子进程
sem_op(semid, -1); // P操作:获取资源
printf("子进程获得资源\n");
sleep(2); // 模拟资源使用
printf("子进程释放资源\n");
sem_op(semid, 1); // V操作:释放资源
return 0;
}
// 父进程
sem_op(semid, -1); // P操作:获取资源
printf("父进程获得资源\n");
sleep(2); // 模拟资源使用
printf("父进程释放资源\n");
sem_op(semid, 1); // V操作:释放资源
wait(NULL);
semctl(semid, 0, IPC_RMID); // 删除信号量
return 0;
}
编译运行:
gcc sem_demo.c -o sem_demo && ./sem_demo
7. 套接字(Socket)
特点:
- 支持跨网络和本地进程通信
- 全双工通信,提供可靠字节流
- 本地通信使用 AF_UNIX 域,通过文件系统标识
示例:本地套接字通信
服务端(socket_server.c):
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#define SOCK_PATH "/tmp/mysock"
int main() {
int server_fd, client_fd;
struct sockaddr_un addr;
char buf[100];
// 创建套接字
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd == -1) { perror("socket"); return 1; }
// 绑定地址
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
unlink(SOCK_PATH); // 确保路径可用
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
// 监听连接
listen(server_fd, 1);
// 接受连接
client_fd = accept(server_fd, NULL, NULL);
read(client_fd, buf, sizeof(buf));
printf("收到: %s\n", buf);
// 清理
close(client_fd);
close(server_fd);
unlink(SOCK_PATH);
return 0;
}
客户端(socket_client.c):
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#define SOCK_PATH "/tmp/mysock"
int main() {
int fd;
struct sockaddr_un addr;
// 创建套接字
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) { perror("socket"); return 1; }
// 连接服务器
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
connect(fd, (struct sockaddr*)&addr, sizeof(addr));
// 发送数据
const char* msg = "Hello from socket client";
write(fd, msg, strlen(msg)+1);
close(fd);
return 0;
}
编译运行:
# 终端1
gcc socket_server.c -o socket_server && ./socket_server
# 终端2
gcc socket_client.c -o socket_client && ./socket_client
总结
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 管道 | 父子进程简单通信 | 简单高效 | 仅限亲缘进程,单向 |
| 命名管道 | 无亲缘关系进程 | 简单,可跨进程 | 单向,需文件系统 |
| 信号 | 事件通知 | 异步,开销小 | 数据量有限 |
| 共享内存 | 大数据传输 | 速度最快 | 需要同步机制 |
| 消息队列 | 异步消息传递 | 按类型接收 | 速度较慢 |
| 信号量 | 进程同步 | 有效控制资源 | 不传递数据 |
| 套接字 | 网络 / 本地通信 | 灵活,全双工 | 开销较大 |
选择 IPC 方法时,需根据进程关系、数据量、同步需求和性能要求综合考虑。

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



