进程间通信(IPC)机制详解
进程间通信(IPC)机制详解
在操作系统中,进程间通信(Inter-Process Communication, IPC)是不同进程间交换数据与同步操作的机制。本文将详细讲解五种常用的IPC方式:管道、信号、共享内存、消息队列和信号量,并附上相关函数的用法示例。
1. 管道(Pipe)
管道是Unix/Linux中最古老的IPC形式,分为匿名管道和命名管道两种。
匿名管道
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd[0]: 读端pipefd[1]: 写端- 返回值:成功返回0,失败返回-1
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int pipefd[2];
char buf[20];
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid_t pid = fork();
if (pid == 0) { // 子进程
close(pipefd[0]); // 关闭读端
write(pipefd[1], "Hello, Parent!", 14);
close(pipefd[1]);
} else { // 父进程
close(pipefd[1]); // 关闭写端
read(pipefd[0], buf, sizeof(buf));
printf("Parent received: %s\n", buf);
close(pipefd[0]);
wait(NULL);
}
return 0;
}
命名管道(FIFO)
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
- 创建命名管道文件
mode参数指定文件权限
使用流程:
- 创建FIFO:
mkfifo("/tmp/myfifo", 0666) - 进程A:
open("/tmp/myfifo", O_WRONLY)并写入数据 - 进程B:
open("/tmp/myfifo", O_RDONLY)并读取数据
2. 信号(Signal)
信号是异步通信机制,用于通知进程发生了某个事件。
常用函数
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
int kill(pid_t pid, int sig);
int raise(int sig);
unsigned int alarm(unsigned int seconds);
常见信号:
SIGINT(2): 终端中断(Ctrl+C)SIGKILL(9): 强制终止SIGSEGV(11): 段错误SIGALRM(14): 定时器信号
示例代码:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int sig) {
printf("Received signal: %d\n", sig);
}
int main() {
signal(SIGINT, handler); // 注册SIGINT处理函数
signal(SIGALRM, handler); // 注册SIGALRM处理函数
alarm(3); // 3秒后发送SIGALRM
while(1) {
pause(); // 等待信号
}
return 0;
}
3. 共享内存(Shared Memory)
共享内存允许多个进程访问同一块内存区域,是最快的IPC方式。
关键函数
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
使用步骤:
- 创建/获取共享内存段:
shmget() - 附加到进程地址空间:
shmat() - 读写共享内存
- 分离共享内存:
shmdt() - 控制共享内存(删除等):
shmctl()
示例代码:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#define SHM_SIZE 1024
int main() {
key_t key = ftok("/tmp", 'A');
int shmid = shmget(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, Shared Memory!");
// 读取数据(在另一个进程中)
// printf("Read from shared memory: %s\n", shm_ptr);
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
return 0;
}
4. 消息队列(Message Queue)
消息队列允许进程通过发送/接收消息来通信。
关键函数
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
消息结构:
struct msgbuf {
long mtype; // 消息类型
char mtext[1]; // 消息数据
};
示例代码:
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype;
char mtext[100];
};
int main() {
key_t key = ftok("/tmp", 'B');
int msgid = msgget(key, IPC_CREAT | 0666);
struct msgbuf msg;
msg.mtype = 1;
strcpy(msg.mtext, "Hello, Message Queue!");
// 发送消息
if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) {
perror("msgsnd");
return 1;
}
// 接收消息(在另一个进程中)
// if (msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) == -1) {
// perror("msgrcv");
// return 1;
// }
// printf("Received: %s\n", msg.mtext);
msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
return 0;
}
5. 信号量(Semaphore)
信号量用于进程间的同步,控制对共享资源的访问。
关键函数
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semctl(int semid, int semnum, int cmd, ...);
操作结构体:
struct sembuf {
unsigned short sem_num; // 信号量编号
short sem_op; // 操作(正数加,负数减)
short sem_flg; // 标志(通常为0)
};
示例代码:
#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <unistd.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
key_t key = ftok("/tmp", 'C');
int semid = semget(key, 1, IPC_CREAT | 0666);
union semun arg;
arg.val = 1; // 初始值设为1(二进制信号量)
semctl(semid, 0, SETVAL, arg);
struct sembuf op_lock = {0, -1, 0}; // P操作
struct sembuf op_unlock = {0, 1, 0}; // V操作
// 加锁
semop(semid, &op_lock, 1);
printf("Critical section start\n");
sleep(2); // 模拟临界区操作
printf("Critical section end\n");
// 解锁
semop(semid, &op_unlock, 1);
semctl(semid, 0, IPC_RMID); // 删除信号量
return 0;
}
IPC方式对比
| 机制 | 速度 | 复杂度 | 适用场景 |
|---|---|---|---|
| 管道 | 中等 | 低 | 父子进程间简单通信 |
| 命名管道 | 中等 | 中 | 任意进程间流式通信 |
| 信号 | 快 | 中 | 事件通知、简单控制 |
| 共享内存 | 最快 | 高 | 大数据量、高性能通信 |
| 消息队列 | 中 | 中高 | 结构化的进程间通信 |
| 信号量 | 快 | 高 | 进程同步、资源访问控制 |
总结
不同的IPC机制适用于不同的场景:
- 管道/命名管道:适合流式数据传输
- 信号:适合事件通知和简单控制
- 共享内存:适合高性能大数据传输
- 消息队列:适合结构化消息传递
- 信号量:适合进程同步和资源保护
在实际系统编程中,通常需要组合多种IPC机制来满足复杂需求。理解各种IPC机制的特点和使用场景,能够帮助开发者设计出高效可靠的进程间通信方案。

浙公网安备 33010602011771号