1、IPC
什么是IPC?Inter Process Communication
2.进程间通信常用的几种方式
1,管道通信:有名管道,无名管道
2,信号- 系统开销小
3,消息队列-内核的链表
4,信号量-计数器
5,共享内存
6,内存映射
7,套接字
3、无名管道
- 管道(匿名管道 pipe)概念补充

-
本质 :内核开辟的一段内存缓冲区 ,以伪文件形式存在。
-
伪文件特点:
-
只存在内存中,不占磁盘空间
-
只用于数据传输,不长期存储
-
-
结构:
-
一对文件描述符:
fd[0]读端、fd[1]写端 -
数据只能从写端进,读端出,单向流动
-
-
生命周期:
- 所有引用该管道的进程全部关闭文件描述符后,管道自动释放
-
阻塞特性(默认):
-
读空管道 → 读阻塞
-
写满管道 → 写阻塞
-
- 管道原理补充
-
内部是环形缓冲区(循环队列)
-
FIFO 先进先出,不能随机访问
-
默认大小:
-
传统 Linux:4KB
-
现代 Linux:默认 16KB,可调整到 64KB
-
-
数据特点:
-
字节流方式传输,无边界
-
不保存数据,读走就没了
-
-
管道局限性补充(你写的很对,我补全)
-
数据只能读一次,不可重复读取读出去就从队列移除,其他进程读不到了。
-
半双工通信
-
同一时刻只能单向传输
-
想双向通信必须创建两个管道
-
-
只能用于有血缘关系的进程 父子、兄弟进程,不支持无亲缘进程通信 → 解决:用 命名管道 FIFO
-
不支持 lseek管道是流式队列,不能定位、不能回退。
-
数据无边界、无消息结构管道只认字节流,不认 "一条消息",需要自己处理分包。
-
容量有限写满就阻塞,不适合超大数据瞬时爆发。
-
额外重要知识点(面试常问)
-
管道的引用计数所有进程都关了对应 fd,管道才真正销毁。
-
管道破裂(SIGPIPE 信号) 读端全部关闭,写端还在写 → 内核发送 SIGPIPE 杀死写进程。
-
原子性 写入小于 PIPE_BUF(通常 4096 字节) 时,写入是原子的,不会穿插。
4、创建匿名通道
int pipe(int fd[2])
fd‐传出参数:
fd[0]‐读端
fd[1]‐写端
返回值:
0:成功‐1:创建失败
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int ret;
int fd[2];
ret = pipe(fd);
if(ret == -1)
{
printf("creat pipe failed\n");
exit(1);
}
printf("pipe[0] is %d\n",fd[0]);
printf("pipe[1] is %d\n",fd[1]);
close(fd[0]);
close(fd[1]);
return 0;
}

因为标准IO里:
- 0:标准输入
- 1:标准输出
- 2:标准错误
- 管道从 3 开始分配
5、父子进程使用管道通信
- 父进程先创建管道
pipe(fd) - 父进程 fork 子进程
- 父子各自关闭不用的端口 (避免混乱)
- 父要写 → 关闭
fd[0]读端 - 子要读 → 关闭
fd[1]写端
- 父要写 → 关闭
- 父进程通过
fd[1]写数据 - 子进程通过
fd[0]读数据 - 关闭文件描述符,结束
管道是半双工,同一时间只能单向传数据。
实现 ps aux| grep "bash" 数据重定向:dup2
#include<stdio.h>
#include<stdlin.h>
#include<unsitd.h>
int main()
{
int ret;
int fd[2];
ret = pipe(fd);
if(ret == -1)
{
printf("pipe failed\n");
exit(1);
}
pid_t pid;
pid = fork();
if(pid == -1)
{
printf("fork failed\n");
exit(1);
}
//ps aux
if(pid > 0)
{
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("ps","ps","aux",NULL);
perror("execlp");
exit(1);
}
//grep "bash"
else if(pid == 0)
{
close(fd[1]);
dup2(fd[0],STDIN_FILENO);
execlp("grep","grep","bash",NULL);
}
printf("fd[0] is %d\n",fd[0]);
printf("fd[1] is %d\n",fd[1]);
close(fd[0]);
close(fd[1]);
return 0;
}

父进程执行 ps aux,输出重定向到管道写端,子进程执行 grep bash,输入从管道读端获取实现:ps aux | grep bash 的效果
先创建通道,再创建子进程
dup2(fd[1], STDOUT_FILENO) 把 标准输出(屏幕) 重定向到 管道写端
意思:ps 输出的内容,不打印到屏幕,而是写入管道!
父进程:ps aux 输出 → 管道写端
管道:写端 → 内核缓冲区 → 读端
子进程:管道读端 → grep bash 读取