引言:为什么进程间通信如此重要
在现代操作系统中,进程是资源分配的基本单位,每个进程拥有独立的虚拟地址空间。这种隔离性保证了系统的稳定性——一个进程的崩溃不会直接影响其他进程。然而,当多个进程需要协同工作时,如何跨越地址空间的边界交换数据,就成了操作系统设计中最核心的问题之一。
Linux 提供了丰富的进程间通信(IPC,Inter-Process Communication)机制,从最古老的管道到现代的 Unix Domain Socket,每一种机制都有其特定的适用场景和性能特征。理解这些机制不仅有助于编写高效的多进程程序,更是深入理解 Linux 内核工作原理的关键路径。
本文将系统性地解析 Linux 下七大 IPC 机制:管道(Pipe)、命名管道(FIFO)、消息队列、共享内存、信号量、Unix Domain Socket 以及信号(Signal),并通过大量 C 语言代码示例展示其在生产环境中的实际用法。

一、管道(Pipe):最经典的 IPC 方式
管道是 Unix 系统中最古老的 IPC 机制,由 Douglas McIlroy 在 1973 年引入。它的核心思想简单而优雅:一个进程的输出直接成为另一个进程的输入。Shell 中的竖线
1 | | |
就是管道最直观的体现。
1.1 匿名管道的原理与限制
匿名管道通过
1 | pipe() |
系统调用创建,在内核中开辟一块环形缓冲区(自 Linux 2.6.11 起默认为 16 页,即 64KB)。它有两个文件描述符:fd[0] 用于读,fd[1] 用于写。管道是半双工的,数据只能单向流动。
1 #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/wait.h>#define BUFFER_SIZE 256int main() { int pipefd[2]; pid_t pid; char buf[BUFFER_SIZE]; if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid == 0) { /* 子进程 - 读端 */ close(pipefd[1]); /* 关闭写端,否则 read 会一直阻塞 */ ssize_t n = read(pipefd[0], buf, BUFFER_SIZE - 1); if (n > 0) { buf[n] = '\0'; printf("子进程收到: %s\n", buf); } close(pipefd[0]); exit(EXIT_SUCCESS); } else { /* 父进程 - 写端 */ close(pipefd[0]); /* 关闭读端 */ const char *msg = "Hello from parent process!"; write(pipefd[1], msg, strlen(msg)); close(pipefd[1]); wait(NULL); /* 等待子进程结束 */ } return 0;}
匿名管道有两个关键限制:第一,它只能在有亲缘关系(fork)的进程间使用;第二,它是半双工的,要实现双向通信需要创建两个管道。这两个限制催生了命名管道的诞生。
1.2 管道的内核实现细节
在内核层面,管道由
1 | struct pipe_inode_info |
管理,内部维护一个环形缓冲区数组。写进程将数据拷贝到内核缓冲区,读进程从内核缓冲区拷贝出来——这意味着管道通信涉及 两次数据拷贝,对大量数据的传输并不高效。
当管道缓冲区满时(写端已写入 64KB 数据未被读取),写进程会在
1 | pipe_wait |
上睡眠,直到读进程消费数据释放空间。反之,缓冲区为空时读进程会阻塞。如果所有写端关闭,读进程的
1 | read() |
返回 0(EOF);如果所有读端关闭,写进程会收到
1 | SIGPIPE |
信号。
二、命名管道(FIFO):无亲缘关系进程的桥梁
命名管道通过
1 | mkfifo() |
或
1 | mknod() |
创建,它在文件系统中有一个真实的路径名。任何知道这个路径名的进程都可以打开它进行读写,从而打破了匿名管道只能在亲缘进程间使用的限制。
1 #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define FIFO_PATH "/tmp/my_fifo"#define BUF_SIZE 1024/* 写入进程 */int writer_main() { mkfifo(FIFO_PATH, 0666); /* 如果已存在不会报错 */ int fd = open(FIFO_PATH, O_WRONLY); if (fd == -1) { perror("open for write"); exit(EXIT_FAILURE); } const char *messages[] = {"消息1: 系统启动", "消息2: 数据加载", "消息3: 服务就绪"}; for (int i = 0; i < 3; i++) { write(fd, messages[i], strlen(messages[i]) + 1); sleep(1); /* 模拟间隔发送 */ } close(fd); return 0;}/* 读取进程 */int reader_main() { int fd = open(FIFO_PATH, O_RDONLY); if (fd == -1) { perror("open for read"); exit(EXIT_FAILURE); } char buf[BUF_SIZE]; while (1) { ssize_t n = read(fd, buf, BUF_SIZE); if (n <= 0) break; printf("收到: %s\n", buf); } close(fd); unlink(FIFO_PATH); /* 清理 */ return 0;}
FIFO 的一个重要特性是:默认情况下
1 | open() |
会阻塞,直到读写两端都被打开。这意味着如果你先启动写进程,它会在
1 | open() |
处等待,直到有读进程打开同一个 FIFO。可以通过
1 | O_NONBLOCK |
标志改变这一行为。
FIFO 常见的生产环境用法包括:Shell 脚本中多个命令之间的数据流水线、守护进程从客户端接收命令、以及 Kubernetes Init Container 之间的配置传递。
三、System V 消息队列:结构化消息传递
消息队列是消息的链表,存放在内核中。与管道的字节流不同,消息队列中的每条消息都有类型(type)字段,接收进程可以根据类型选择性接收——这是消息队列最大的优势。

3.1 消息队列的创建与使用
1 #include <sys/msg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define MSG_SIZE 128/* 消息结构必须以 long type 开头 */struct msgbuf { long mtype; /* 消息类型,必须 > 0 */ char mtext[MSG_SIZE];};int main() { key_t key = ftok("/tmp", 'A'); /* 生成唯一 key */ int msgid = msgget(key, 0666 | IPC_CREAT); if (msgid == -1) { perror("msgget"); exit(EXIT_FAILURE); } /* 发送消息 */ struct msgbuf msg; msg.mtype = 1; /* 类型1:普通消息 */ strncpy(msg.mtext, "这是一条普通优先级消息", MSG_SIZE); if (msgsnd(msgid, &msg, sizeof(msg.mtext), 0) == -1) { perror("msgsnd"); exit(EXIT_FAILURE); } msg.mtype = 2; /* 类型2:高优先级消息 */ strncpy(msg.mtext, "这是一条高优先级消息", MSG_SIZE); msgsnd(msgid, &msg, sizeof(msg.mtext), 0); /* 按类型接收 - 先收高优先级 */ if (msgrcv(msgid, &msg, MSG_SIZE, 2, 0) != -1) { printf("收到类型2消息: %s\n", msg.mtext); } /* 再收普通优先级 */ if (msgrcv(msgid, &msg, MSG_SIZE, 1, 0) != -1) { printf("收到类型1消息: %s\n", msg.mtext); } /* 删除消息队列 */ msgctl(msgid, IPC_RMID, NULL); return 0;}
3.2 消息队列的高级用法
1 | msgrcv() |
的
1 | msgtyp |
参数支持三种模式:
- msgtyp == 0:接收队列中第一条消息(FIFO)
- msgtyp > 0:接收类型等于 msgtyp 的第一条消息
- msgtyp < 0:接收类型小于等于 |msgtyp| 的最低类型值消息(优先级队列)
第三种模式可以实现优先级队列:发送时用较小的 mtype 表示高优先级,接收时传
1 | msgtyp = -MAX_PRIORITY |
,系统会优先返回最低类型号(最高优先级)的消息。
可以通过
1 | msgctl(msgid, IPC_STAT, &buf) |
查看队列状态:
1 | msg_qbytes |
是队列最大字节数(默认 16KB,可通过
1 | /proc/sys/kernel/msgmnb |
调整),
1 | msg_qnum |
是当前消息数,
1 | msg_lspid |
/
1 | msg_lrpid |
是最后发送/接收进程的 PID。
四、共享内存:最快的 IPC 方式
共享内存是 Linux 下最快的 IPC 机制,因为数据不需要在内核和用户空间之间拷贝——多个进程直接映射同一块物理内存到各自的虚拟地址空间。这就像两个人共享同一块白板,一个人写的内容另一个人立即能看到。

4.1 System V 共享内存
1 #include <sys/shm.h>#include <sys/ipc.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define SHM_SIZE 4096int main() { key_t key = ftok("/tmp", 'B'); int shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT); if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); } /* 将共享内存附加到进程地址空间 */ char *shmaddr = shmat(shmid, NULL, 0); if (shmaddr == (char *)-1) { perror("shmat"); exit(EXIT_FAILURE); } /* 写入数据 */ strncpy(shmaddr, "Hello from shared memory!", SHM_SIZE); /* 分离共享内存 */ shmdt(shmaddr); /* 另一个进程可以附加并读取 */ /* ... */ /* 删除共享内存 */ shmctl(shmid, IPC_RMID, NULL); return 0;}
4.2 POSIX 共享内存(推荐)
POSIX 共享内存通过
1 | shm_open() |
创建,使用
1 | mmap() |
映射,API 更加现代且与文件操作一致,是新项目的推荐选择。
1 #include <sys/mman.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define SHM_NAME "/my_shm"#define SHM_SIZE 4096/* 写入进程 */int writer() { int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); if (fd == -1) { perror("shm_open"); exit(1); } ftruncate(fd, SHM_SIZE); void *ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap"); exit(1); } /* 写入结构化数据 */ typedef struct { int version; int count; double values[100]; } SharedData; SharedData *data = (SharedData *)ptr; data->version = 1; data->count = 3; data->values[0] = 3.14; data->values[1] = 2.72; data->values[2] = 1.41; close(fd); return 0;}/* 读取进程 */int reader() { int fd = shm_open(SHM_NAME, O_RDONLY, 0666); if (fd == -1) { perror("shm_open"); exit(1); } void *ptr = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap"); exit(1); } SharedData *data = (SharedData *)ptr; printf("版本: %d, 数据量: %d\n", data->version, data->count); for (int i = 0; i < data->count; i++) { printf(" values[%d] = %f\n", i, data->values[i]); } munmap(ptr, SHM_SIZE); close(fd); shm_unlink(SHM_NAME); /* 清理 */ return 0;}
共享内存的致命问题是同步——当两个进程同时读写同一块内存时,没有任何机制保证数据的一致性。这就是信号量存在的意义。
五、信号量:IPC 的同步守护者
信号量(Semaphore)不是用来传递数据的,而是用来控制对共享资源的访问顺序。它由 Dijkstra 在 1965 年提出,核心操作是 P(等待/减1)和 V(释放/加1)。
5.1 System V 信号量
System V 信号量以信号量集(semaphore set)为单位,一次可以操作多个信号量。虽然功能强大,但 API 设计较为复杂。
1 #include <sys/sem.h>#include <sys/ipc.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>/* System V 信号量操作需要 union semun */#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)/* union semun 已在 sys/sem.h 中定义 */#elseunion semun { int val; struct semid_ds *buf; unsigned short *array;};#endifint main() { key_t key = ftok("/tmp", 'C'); int semid = semget(key, 1, 0666 | IPC_CREAT); if (semid == -1) { perror("semget"); exit(1); } /* 初始化信号量值为 1(互斥锁) */ union semun arg; arg.val = 1; semctl(semid, 0, SETVAL, arg); /* P 操作(等待/加锁) */ struct sembuf sop_p = {0, -1, SEM_UNDO}; /* SEM_UNDO: 进程崩溃自动释放 */ semop(semid, &sop_p, 1); printf("进程 %d 获得锁,进入临界区\n", getpid()); sleep(2); /* 模拟临界区操作 */ printf("进程 %d 释放锁\n", getpid()); /* V 操作(释放/解锁) */ struct sembuf sop_v = {0, 1, SEM_UNDO}; semop(semid, &sop_v, 1); semctl(semid, 0, IPC_RMID); return 0;}
5.2 POSIX 信号量(推荐)
POSIX 信号量 API 更简洁,分为有名信号量(跨进程)和无名信号量(同进程内线程间)。
1 #include <semaphore.h>#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <sys/mman.h>#include <unistd.h>#define SEM_NAME "/my_sem"#define SHM_NAME "/my_shm_sem"/* 生产者-消费者模型示例 */typedef struct { int buffer[10]; int in; int out;} SharedBuffer;int producer() { sem_t *empty = sem_open("/sem_empty", O_CREAT, 0666, 10); /* 空槽位数 */ sem_t *full = sem_open("/sem_full", O_CREAT, 0666, 0); /* 数据个数 */ sem_t *mutex = sem_open("/sem_mutex", O_CREAT, 0666, 1); /* 互斥锁 */ int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); ftruncate(fd, sizeof(SharedBuffer)); SharedBuffer *buf = mmap(NULL, sizeof(SharedBuffer), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); for (int i = 0; i < 20; i++) { sem_wait(empty); /* 等待空槽位 */ sem_wait(mutex); /* 获取互斥锁 */ buf->buffer[buf->in] = i; printf("生产: %d 放入位置 %d\n", i, buf->in); buf->in = (buf->in + 1) % 10; sem_post(mutex); /* 释放互斥锁 */ sem_post(full); /* 增加数据计数 */ usleep(100000); /* 模拟生产耗时 */ } munmap(buf, sizeof(SharedBuffer)); close(fd); return 0;}int consumer() { sem_t *empty = sem_open("/sem_empty", 0); sem_t *full = sem_open("/sem_full", 0); sem_t *mutex = sem_open("/sem_mutex", 0); int fd = shm_open(SHM_NAME, O_RDWR, 0666); SharedBuffer *buf = mmap(NULL, sizeof(SharedBuffer), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); for (int i = 0; i < 20; i++) { sem_wait(full); /* 等待有数据可消费 */ sem_wait(mutex); /* 获取互斥锁 */ int item = buf->buffer[buf->out]; printf("消费: 从位置 %d 取出 %d\n", buf->out, item); buf->out = (buf->out + 1) % 10; sem_post(mutex); /* 释放互斥锁 */ sem_post(empty); /* 增加空槽位计数 */ usleep(200000); /* 模拟消费耗时 */ } munmap(buf, sizeof(SharedBuffer)); close(fd); return 0;}
六、Unix Domain Socket:最灵活的本地 IPC
Unix Domain Socket(UDS)结合了网络 Socket 的灵活 API 和本地通信的高性能。与网络 Socket 不同,UDS 不经过网络协议栈,数据直接在内核缓冲区之间拷贝,延迟极低。它支持流式(SOCK_STREAM)和报文式(SOCK_DGRAM)两种模式,还能通过
1 | sendmsg()/recvmsg() |
传递文件描述符——这是其他 IPC 机制无法做到的。
6.1 流式 Unix Domain Socket 示例
1 #include <sys/socket.h>#include <sys/un.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define SOCK_PATH "/tmp/my_usock"#define BUF_SIZE 1024int server() { int sfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sfd == -1) { perror("socket"); exit(1); } struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1); unlink(SOCK_PATH); /* 绑定前移除旧文件 */ if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); exit(1); } if (listen(sfd, 5) == -1) { perror("listen"); exit(1); } while (1) { int cfd = accept(sfd, NULL, NULL); if (cfd == -1) continue; char buf[BUF_SIZE]; ssize_t n = recv(cfd, buf, BUF_SIZE - 1, 0); if (n > 0) { buf[n] = '\0'; printf("服务器收到: %s\n", buf); send(cfd, "ACK", 3, 0); } close(cfd); } close(sfd); return 0;}int client() { int sfd = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1); if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("connect"); exit(1); } send(sfd, "Hello from Unix Domain Socket!", 31, 0); char buf[BUF_SIZE]; ssize_t n = recv(sfd, buf, BUF_SIZE - 1, 0); if (n > 0) { buf[n] = '\0'; printf("客户端收到响应: %s\n", buf); } close(sfd); return 0;}
6.2 传递文件描述符:UDS 的杀手级特性
通过
1 | sendmsg() |
和
1 | recvmsg() |
的辅助数据(ancillary data),UDS 可以在进程间传递打开的文件描述符。这在实现特权分离架构时非常有用——例如,主进程以 root 权限打开日志文件,然后将文件描述符传递给低权限工作进程。
1 /* 传递文件描述符的核心代码 */void send_fd(int sock, int fd_to_send) { struct msghdr msg = {0}; struct iovec iov = { .iov_base = "X", .iov_len = 1 }; msg.msg_iov = &iov; msg.msg_iovlen = 1; char cmsgbuf[CMSG_SPACE(sizeof(int))]; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); *(int *)CMSG_DATA(cmsg) = fd_to_send; sendmsg(sock, &msg, 0);}int recv_fd(int sock) { struct msghdr msg = {0}; char buf[1]; struct iovec iov = { .iov_base = buf, .iov_len = 1 }; msg.msg_iov = &iov; msg.msg_iovlen = 1; char cmsgbuf[CMSG_SPACE(sizeof(int))]; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); recvmsg(sock, &msg, 0); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { return *(int *)CMSG_DATA(cmsg); } return -1;}
七、各 IPC 机制对比与选型指南
面对多种 IPC 机制,如何选择?关键要看数据量、同步需求、进程关系和性能要求。
| IPC 机制 | 数据拷贝次数 | 是否需要同步 | 跨亲缘关系 | 适用数据量 | 典型场景 |
|---|---|---|---|---|---|
| 匿名管道 | 2次 | 内核保证 | 否 | 小-中 | Shell 管道、父子进程通信 |
| 命名管道 FIFO | 2次 | 内核保证 | 是 | 小-中 | 无亲缘进程简单通信 |
| 消息队列 | 2次 | 内核保证 | 是 | 小-中 | 优先级消息、请求-响应 |
| 共享内存 | 0次 | 需要信号量 | 是 | 大 | 高性能数据共享、大缓冲区 |
| 信号量 | N/A | N/A | 是 | N/A | 资源互斥、同步控制 |
| Unix Domain Socket | 2次 | 协议保证 | 是 | 小-中 | 客户端-服务器、传递 fd |
| 信号 Signal | N/A | N/A | 是 | N/A | 异步通知、进程控制 |
7.1 选型决策树
- 只需要简单数据流传递? → 管道(有亲缘)或 FIFO(无亲缘)
- 需要按优先级接收消息? → 消息队列
- 需要传输大量数据(MB级以上)? → 共享内存 + 信号量
- 需要传递文件描述符? → Unix Domain Socket
- 需要客户端-服务器模型? → Unix Domain Socket
- 只需要通知事件发生? → 信号
- 不确定? → Unix Domain Socket(最通用)
八、生产环境中的 IPC 实战技巧
8.1 防止资源泄漏
System V IPC 对象(消息队列、共享内存、信号量)在进程退出后不会自动删除,必须显式调用
1 | xxxctl(..., IPC_RMID, ...) |
。一个常见的问题是开发过程中崩溃的进程留下了大量 IPC 对象。可以使用以下命令清理:
1 # 查看所有 System V IPC 资源ipcs -a# 删除指定消息队列ipcrm -q <msqid># 删除指定共享内存ipcrm -m <shmid># 删除指定信号量ipcrm -s <semid># 一键清理当前用户的所有 IPC 资源ipcs -q | awk '/^0x/ {print $2}' | xargs -I{} ipcrm -q {}ipcs -m | awk '/^0x/ {print $2}' | xargs -I{} ipcrm -m {}ipcs -s | awk '/^0x/ {print $2}' | xargs -I{} ipcrm -s {}
8.2 System V IPC 的内核参数调优
生产环境中可能需要调整以下内核参数来满足高并发需求:
1 # /etc/sysctl.conf 或 /proc/sys/kernel/# 单个消息的最大字节数(默认 8192)kernel.msgmax = 65536# 消息队列的最大总字节数(默认 16384)kernel.msgmnb = 1048576# 系统级消息队列总数上限(默认 32000)kernel.msgmni = 10240# 单个共享内存段的最大大小(默认 32MB on x86_64)kernel.shmmax = 1073741824 # 1GB# 系统级共享内存总页数(默认 2097152)kernel.shmall = 4194304# 应用修改sysctl -p
8.3 使用 systemd 自动清理 POSIX IPC 资源
POSIX 共享内存和信号量存放在
1 | /dev/shm/ |
目录下,可以设置 systemd 的
1 | RemoveIPC=yes |
来在用户完全注销后自动清理:
1 # /etc/systemd/logind.confRemoveIPC=yes
注意:此选项会在最后一个用户会话结束时删除该用户的所有 POSIX IPC 对象。如果守护进程以用户身份运行且需要持久的 IPC 对象,请确保使用
1 | loginctl enable-linger |
保持会话存活。
8.4 调试 IPC 问题的实用方法
当 IPC 通信出现问题时,以下工具和方法可以帮助定位:
-
1strace -e trace=pipe,pipe2,shmget,shmat,msgsnd,msgrcv
— 跟踪进程的 IPC 系统调用
-
1ipcs -i <id>
— 查看 IPC 对象的详细信息(属主、权限、最后操作时间等)
-
1/proc/sysvipc/
— 内核暴露的 IPC 状态文件,包含更底层的调试信息
-
1lsof -U
— 列出所有打开的 Unix Domain Socket
-
1ss -x
— 显示 Unix Domain Socket 的连接状态(替代 netstat)
结语
进程间通信是操作系统最核心的基础设施之一。理解每种 IPC 机制的原理、性能特征和适用场景,是系统程序员的基本功。在实际项目中:
- 管道和 FIFO 适合简单的数据流传递,Shell 管道就是最典型的应用
- 消息队列适合需要消息类型区分和优先级调度的场景
- 共享内存是大数据量传输的唯一选择,但必须搭配信号量保证同步
- Unix Domain Socket 是最灵活的本地 IPC,特别适合 C/S 架构和需要传递文件描述符的场景
最后提醒:在容器化环境中,System V IPC 的 namespace 隔离由
1 | ipc namespace |
控制(
1 | docker run --ipc=host |
可以共享宿主机的 IPC namespace),而 POSIX IPC 基于文件系统默认在
1 | /dev/shm |
中,需要通过挂载命名空间隔离。理解这些边界条件,才能在生产环境中正确使用 IPC。
汤不热吧