一、IPC 基本概念
定义:进程间通信(InterProcess Communication,IPC)是指进程与进程之间交换信息的过程。
常用通信方式:
- 无名管道(pipe)
- 有名管道(fifo)
- 信号(signal)
- 共享内存 (mmap)
- 套接字(socket)
二、无名管道(pipe)
(一)特点
- 只能用于具有亲缘关系的进程之间的通信(如父子进程、兄弟进程)
- 单工通信模式,具有固定的读端和写端
- 创建时返回两个文件描述符:
pfd[0]用于读,pfd[1]用于写
(二)创建函数
#include <unistd.h>
int pipe(int pfd[2]);
- 参数:
pfd为包含两个整数的数组,用于存储文件描述符 - 返回值:成功返回 0,失败返回 - 1
(三)代码示例 1:基本父子进程通信
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int pfd[2]; // 文件描述符
int re; // 返回值
char buf[20] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid == 0) {
// 子进程:写管道
while (1) {
strcpy(buf, "hhahahahah");
write(pfd[1], buf, strlen(buf));
sleep(1);
}
} else {
// 父进程:读管道
while (1) {
re = read(pfd[0], buf, 20);
if (re > 0) {
printf("read pipe=%s\n", buf);
}
}
}
}
运行结果截图说明
read pipe=hhahahahah
read pipe=hhahahahah
read pipe=hhahahahah
read pipe=hhahahahah
- 截图显示父进程持续从管道读取子进程写入的数据。
(四)代码示例 2:多进程共享管道(父进程读,两子进程写)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int pfd[2];
int i;
int re;
char buf[40] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
printf("%d,%d\n", pfd[0], pfd[1]);
for (i = 0; i < 2; i++) {
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid > 0) {
// 父进程继续创建子进程
} else {
// 子进程跳出循环
break;
}
}
if (i == 2) { // 父进程
close(pfd[1]);
while (1) {
memset(buf, 0, 40);
re = read(pfd[0], buf, 40);
if (re > 0) {
printf("%s\n", buf);
}
}
return 0;
}
if (i == 1) { // 子进程1
close(pfd[0]);
while (1) {
strcpy(buf, "this is 2 process");
write(pfd[1], buf, strlen(buf));
usleep(930000);
}
return 0;
}
if (i == 0) { // 子进程0
close(pfd[0]);
while (1) {
strcpy(buf, "this is 1 process");
write(pfd[1], buf, strlen(buf));
sleep(1);
}
return 0;
}
}
运行结果截图说明
3,4
this is 1 processthis is 2 process
this is 2 process
this is 1 process
this is 2 process
this is 1 process
this is 2 process
this is 1 process
this is 2 process
this is 1 process
this is 2 process
this is 1 process
this is12 process
this is 1 process
this is 2 process
this is 1 process
- 截图显示父进程同时读取两个子进程写入管道的数据,数据交替输出。
(五)无名管道读写特性
1. 读管道
- 管道中有数据:
read返回实际读到的字节数。 - 管道中无数据:
- 写端全部关闭:
read返回 0(类似文件结尾)。 - 写端未全部关闭:
read阻塞等待。
- 写端全部关闭:
2. 写管道
- 读端全部关闭:进程异常终止(可通过捕捉
SIGPIPE信号避免终止)。 - 读端未全部关闭:
- 管道已满(64K):
write阻塞。 - 管道未满:
write写入数据并返回实际字节数。
- 管道已满(64K):
(六)读写特性代码示例
1. 写端关闭时读管道
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pfd[2];
int re;
char buf[20] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
printf("%d,%d\n", pfd[0], pfd[1]);
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid > 0) {
// 父进程关闭读写端
close(pfd[0]);
close(pfd[1]);
int j = 0;
while (1) {
j++;
strcpy(buf, "hhahahahah");
sleep(1);
}
} else {
// 子进程关闭写端
close(pfd[1]);
sleep(30000);
exit(0);
while (1) {
re = read(pfd[0], buf, 20);
if (re > 0) {
printf("read pipe=%s\n", buf);
} else if (re == 0) {
printf("re=0\n");
}
}
}
}
运行结果截图说明
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
re=0
- 截图显示当管道写端全部关闭后,读端
read返回 0。
2. 写端未关闭时读管道(阻塞)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pfd[2];
int re;
char buf[20] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
printf("%d,%d\n", pfd[0], pfd[1]);
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid > 0) {
// 父进程写管道
close(pfd[0]);
while (1) {
strcpy(buf, "hhahahahah");
write(pfd[1], buf, strlen(buf));
sleep(1);
}
} else {
// 子进程关闭写端并休眠
close(pfd[1]);
sleep(30000);
exit(0);
while (1) {
re = read(pfd[0], buf, 20);
if (re > 0) {
printf("read pipe=%s\n", buf);
} else if (re == 0) {
printf("re=0\n");
}
}
}
}
3. 读端关闭时写管道(进程终止)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pfd[2];
int re;
char buf[20] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
printf("%d,%d\n", pfd[0], pfd[1]);
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid > 0) {
// 父进程写管道
close(pfd[0]);
while (1) {
strcpy(buf, "hhahahahah");
write(pfd[1], buf, strlen(buf));
sleep(1);
}
} else {
// 子进程关闭读写端
close(pfd[1]);
close(pfd[0]);
sleep(30000);
exit(0);
while (1) {
re = read(pfd[0], buf, 20);
if (re > 0) {
printf("read pipe=%s\n", buf);
} else if (re == 0) {
printf("re=0\n");
}
}
}
}
4. 管道已满时写阻塞
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pfd[2];
int re;
char buf[20] = {0};
pid_t pid;
re = pipe(pfd);
if (re < 0) {
perror("pipe");
return 0;
}
printf("%d,%d\n", pfd[0], pfd[1]);
pid = fork();
if (pid < 0) {
perror("fork");
return 0;
} else if (pid > 0) {
// 父进程大量写入数据
close(pfd[0]);
int j = 0;
while (1) {
j++;
strcpy(buf, "hhahahahah");
for (int i = 0; i < 1000; i++) {
write(pfd[1], buf, strlen(buf));
}
printf("write %d times\n", j);
sleep(1);
}
} else {
// 子进程关闭写端并休眠
close(pfd[1]);
sleep(30000);
exit(0);
while (1) {
re = read(pfd[0], buf, 20);
if (re > 0) {
printf("read pipe=%s\n", buf);
} else if (re == 0) {
printf("re=0\n");
}
}
}
}
三、有名管道(FIFO)
(一)创建函数
#include <unistd.h>
#include <fcntl.h>
int mkfifo(const char *path, mode_t mode);
- 参数:
path:管道文件路径mode:管道文件权限(如 0666)
- 返回值:成功返回 0,失败返回 - 1
(二)打开方式
open(const char *path, O_RDONLY);open(const char *path, O_RDONLY | O_NONBLOCK);open(const char *path, O_WRONLY);open(const char *path, O_WRONLY | O_NONBLOCK);
(三)特点
- 支持非亲缘关系进程通信
- 通过路径名操作,文件系统中可见但内容存于内存
- 使用文件 IO 操作
- 遵循先进先出(FIFO)规则
- 不支持
lseek操作 - 单工读写
(四)代码示例 1:写端程序
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main() {
int re;
int fd;
char buf[32];
// 创建有名管道
re = mkfifo("/myfifo", 0666);
if (re < 0) {
perror("mkfifo");
}
// 以只写方式打开管道
fd = open("/myfifo", O_WRONLY);
if (fd < 0) {
perror("open");
return 0;
}
printf("after open\n");
// 从标准输入读取数据并写入管道
while (1) {
fgets(buf, 32, stdin);
write(fd, buf, strlen(buf));
}
}
(五)代码示例 2:读端程序
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main() {
int re;
int fd;
char buf[32];
// 以只读方式打开管道
fd = open("/myfifo", O_RDONLY);
if (fd < 0) {
perror("open");
return 0;
}
printf("after open\n");
// 从管道读取数据并输出
while (1) {
re = read(fd, buf, 32);
if (re > 0) {
printf("read fifo=%s\n", buf);
} else if (re == 0) {
exit(0);
}
}
}
(六)注意事项
- 不建议以
O_RDWR模式打开 FIFO 文件,行为未明确定义。 O_NONBLOCK选项:- 读打开时:非阻塞模式下,若无写端打开仍返回成功;阻塞模式下,等待写端打开。
- 写打开时:非阻塞模式下,若无读端打开返回 - 1;阻塞模式下,等待读端打开。
- 数据完整性:多个进程写同一管道时,若写入数据长度≤
PIPE_BUF(4K),系统确保数据不交错。
6628

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



