进程间通信(IPC)

一、IPC 机制总览

机制 范围 特点 用途
管道(pipe) 父子/兄弟 字节流、半双工 shell
命名管道(FIFO) 任意进程 文件系统节点 简单通信
消息队列 任意进程 消息边界、优先级 SysV/POSIX
信号量 任意进程 同步原语 互斥/计数
共享内存 任意进程 最快、需要同步 大数据传输
信号(signal) 任意进程 异步通知 事件
socketpair 父子/兄弟 全双工 fork 服务
Unix domain socket 任意进程 本机高性能 微服务
文件 任意进程 简单、需要 flock 配置、日志
mmap 临时文件 任意进程 简单高效 大量数据
D-Bus 任意进程 高层、对象化 桌面/Linux 服务

二、管道 pipe

1. 匿名管道

#include <unistd.h>

int fds[2];
pipe(fds);
pid_t pid = fork();
if (pid == 0) {
    close(fds[1]);
    char buf[128];
    read(fds[0], buf, sizeof(buf));   // 子进程读
    close(fds[0]);
    _exit(0);
} else {
    close(fds[0]);
    write(fds[1], "hello", 5);          // 父进程写
    close(fds[1]);
    wait(NULL);
}

2. 命名管道(FIFO)

mkfifo /tmp/myfifo
echo "hi" > /tmp/myfifo
cat /tmp/myfifo
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "data", 4);
close(fd);

3. shell 用法

ls | grep "\.c$" | wc -l
cmd1 < input > output 2> err

三、System V IPC

1. 共享内存

#include <sys/shm.h>

key_t key = ftok("/tmp", 'A');
int shmid = shmget(key, 4096, IPC_CREAT | 0666);
void *p = shmat(shmid, NULL, 0);
strcpy(p, "hello");
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);          // 删除

2. 消息队列

struct msgbuf {
    long mtype;
    char mtext[100];
};

int msqid = msgget(key, IPC_CREAT | 0666);
msgsnd(msqid, &msg, strlen(msg.mtext)+1, 0);
msgrcv(msqid, &msg, sizeof(msg.mtext), 1, 0);  // mtype=1
msgctl(msqid, IPC_RMID, NULL);

3. 信号量

#include <sys/sem.h>

int semid = semget(key, 1, IPC_CREAT | 0666);
semctl(semid, 0, SETVAL, 1);             // 初值 1

struct sembuf sb = { .sem_num = 0, .sem_op = -1, .sem_flg = 0 };
semop(semid, &sb, 1);                     // P

sb.sem_op = 1;
semop(semid, &sb, 1);                     // V

四、POSIX IPC(推荐,更现代)

1. POSIX 共享内存

#include <fcntl.h>
#include <sys/mman.h>

int fd = shm_open("/my_shm", O_CREAT|O_RDWR, 0666);
ftruncate(fd, SIZE);
void *p = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 使用 *p
munmap(p, SIZE);
close(fd);
shm_unlink("/my_shm");

2. POSIX 消息队列

#include <mqueue.h>

mqd_t mq = mq_open("/my_mq", O_CREAT|O_RDWR, 0666, NULL);
struct mq_attr attr;
mq_getattr(mq, &attr);
char buf[1024];
mq_send(mq, "hello", 5, 0);
mq_receive(mq, buf, sizeof(buf), NULL);
mq_close(mq);
mq_unlink("/my_mq");

3. POSIX 信号量

06-thread.md

五、信号 Signal

1. 常用信号

信号 编号 默认动作 含义
SIGHUP 1 Term 终端断开 / 重载配置
SIGINT 2 Term Ctrl+C
SIGQUIT 3 Core Ctrl+\
SIGILL 4 Core 非法指令
SIGABRT 6 Core abort
SIGFPE 8 Core 算术错(除零)
SIGKILL 9 Term 必杀(不可捕获)
SIGSEGV 11 Core 段错误
SIGPIPE 13 Term 管道破裂
SIGALRM 14 Term alarm
SIGTERM 15 Term 优雅退出
SIGCHLD 17 Ign 子进程状态变化
SIGSTOP 19 Stop 必停(不可捕获)
SIGTSTP 20 Stop Ctrl+Z
SIGIO / SIGPOLL 29 Term I/O 可用
SIGSYS 31 Core 非法系统调用

2. 处理函数

void handler(int sig) {
    // 仅 async-signal-safe 函数可用!
    // 不能 printf / malloc / pthread_* / stdio
    const char *msg = "got\n";
    write(STDOUT_FILENO, msg, 4);
}

struct sigaction sa = {
    .sa_handler = handler,
    .sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);

3. 信号屏蔽

sigset_t set, old;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, &old);       // 屏蔽
sigprocmask(SIG_SETMASK, &old, NULL);      // 恢复

4. 实时信号(SIGRTMIN..SIGRTMAX)

5. 注意事项

六、socketpair / Unix Domain Socket

1. socketpair

int sv[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
// 类似 pipe,但全双工

2. Unix Domain Socket

// server
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
unlink("/tmp/my.sock");
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/my.sock");
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 5);
int cfd = accept(fd, NULL, NULL);
// ...

// client
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/my.sock");
connect(fd, (struct sockaddr*)&addr, sizeof(addr));

3. abstract namespace(Linux 特性)

addr.sun_family = AF_UNIX;
addr.sun_path[0] = '\0';   // 首个字节 0 = abstract
strcpy(addr.sun_path + 1, "myabstract");

七、选型建议

场景 推荐
父子进程字节流 pipe
命令行连接 shell pipe
任意进程大数据传输 共享内存 + 信号量
任意进程消息 POSIX 消息队列 / Unix Socket
异步事件 signal / signalfd
微服务间通信 Unix Socket / D-Bus
跨机器 TCP Socket