1.进程间通信常用的方式
1
,管道通信:有名管道,无名管道
2
,信号
-
系统开销小
3
,消息队列
-
内核的链表
4
,信号量
-
计数器
5
,共享内存
6
,内存映射
7
,套接字
2.管道的概念
![](https://i-blog.csdnimg.cn/direct/4b72c87dd3f9471787f5c200e48e3d9f.png)
2.1本质
*内核缓冲区
*伪文件
-
不占用磁盘空间
2.2特点
*两部分: 读端,写端,对应两个文件描述符 ,分别是数据写端流入,读端流出
*
操作管理的进程被销毁之后
,
管道自动被释放
*管道默认是阻塞的
*
管道数据只能被读取一次,再次读取数据需要再次写入数据
2.3管道的原理
*内部实现方式:队列 ,环形队列
*特点:先进先出
*缓冲区大小 :默认4K ,大小会根据实际情况做适当调整
2.4管道的局限性
*队列: 数据只能读取一次,不能重复读取
*单工:遥控器
*半双工:对讲机
2.5管道的读写行为
读操作:
有数据 :read(fd[1]) 正常读,返回读出的字节数
无数据:如果写端被全部关闭时,read
返回
0
,相当于读文件到了尾部。
如果写端没有全部关闭时,read阻塞
写操作:
读端全部关闭:
管道不断的写入数据可能管道会破裂,进程被终止 ,
就是内核给当前进程发送信号SIGPIPE- 13,默认处理动作
读端没全部关闭:
缓冲区写满了:write
阻塞
缓冲区没满 :write
继续写,直到写满,阻塞
2.6查看管道缓冲区大小
命令 :ulimit -a
![](https://i-blog.csdnimg.cn/direct/550cdbe8488e47199c5e4265def6c0bc.png)
3.无名管道
3.1无名管道的创建
函数:int pipe(int fd[2])
fd‐传出参数:
fd[0]‐
读端
fd[1]‐
写端
返回值:
0
:成功
‐1
:创建失败
历程:
include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2];
int rnt;
rnt = pipe(fd);
if(rnt == -1)
{
printf("创建无名管道失败\n");
perror("pipe error");
exit(0);
}
else
{
printf("读端:%d\n",fd[0]);
printf("写端:%d\n",fd[1]);
}
close(fd[0]);
close(fd[1]);
return 0;
}
运行结果:
3.2父子进程使用无名管道通信
示例:实现
ps aux| grep "bash"
示例功能:父进程使用 ps aux命令进行查询,子进程使用grep "bash"进行读取
数据重定向:
dup2
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int fd[2];
int rnt;
rnt = pipe(fd);
if(rnt == -1)
{
printf("创建无名管道失败\n");
perror("pipe error");
exit(0);
}
pid_t pid = fork();
if(pid == -1)
{
printf("fork failed!\n");
perror("fork");
exit(0);
}
if(pid == 0)//子进程进行读取数据,grep "bash"
{
printf("这是一个子进程\n");
close(fd[1]);
dup2(fd[0],STDIN_FILENO);//将要在终端读取数据,重定向到管道去读取
execlp("grep","grep","bash","--color=auto",NULL);
exit(0);
}
if(pid > 0)//父进程将数据写到管道里去 ps aux
{
close(fd[0]);//写之前要把管道的读关闭
printf("这是一个父进程\n");
dup2(fd[1],STDOUT_FILENO);//将数据重定向到管道去 不然就显示到终端了
execlp("ps","ps","aux",NULL);
wait(NULL);//阻塞回收子进程
exit(0);
}
else
{
printf("读端:%d\n",fd[0]);
printf("写端:%d\n",fd[1]);
}
close(fd[0]);
close(fd[1]);
return 0;
}
运行结果:
![](https://i-blog.csdnimg.cn/direct/fe67da8f5a8e4f05a42358a5c8becb51.png)
PS:进行管道读写的时候,读的时候要把写关闭,写的时候要把读关闭
3.3使用场景
有血缘关系的进程间通信(如:父子进程之间的通信)
4.有名管道
4.1有名管道的创建
函数形式:
int mkfifo(const char \*filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和
umask
有关系。
返回值:创建成功返回
0
,创建失败返回
-1
。
e--1
write --2
read --4
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int ret;
ret = mkfifo("/home/czx/mkfifo",0777);
if(ret == -1)
{
printf("有名管道创建失败\n");
perror("myfifo");
return -1;
}
else
printf("有名管道创建成功\n");
return 0;
}
~
运行结果:
PS :创建管道的时候,只会创建一个指向内核缓冲区的节点,调用open()的时候才会生成管道.
示例:
功能:一个进程利用管道进行读取数据,一个进程利用管道进行写数据
利用管道进行读数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int ret;
char readBuff[20] = {0};
int read_ret;
int fd;
ret = mkfifo("/home/czx/mkfifo",0775);
if(ret == -1)
{
printf("有名管道创建失败\n");
perror("myfifo");
return -1;
}
else
{
printf("有名管道创建成功\n");
fd = open("/home/czx/mkfifo",O_RDONLY);
if(fd < 0)
{
printf("打开管道错误\n");
return -2;
}
else
{
while(1)
{
read_ret = read(fd,readBuff,20);
printf("读取到的字节数%d,内容为:%s\n",read_ret,readBuff);
}
}
}
close(fd);
return 0;
}
利用管道进行写数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char writeBuff[20];
int fd;
fd = open("/home/czx/mkfifo",O_WRONLY);
if(fd < 0)
{
printf("打开管道错误\n");
return -2;
}
else
{
while(1)
{
gets(writeBuff);
write(fd,writeBuff,20);
}
}
close(fd);
return 0;
}
~
运行结果:
![](https://i-blog.csdnimg.cn/direct/55b53f81f4b147eaba5d50d01b63a0f5.png)
4.2特点
有名管道
在磁盘上有这样一个文件
ls -l ->p
也是一个伪文件,在磁盘大小永久为
0
数据存在内核中有一个对应的缓冲区
半双工通信方式
4.3使用场景
没有血缘关系的进程间通信
4.4创建方式
命令:
mkfifo
管道名
函数:
mkfifo
4.5.fifo文件可以使用io函数进程操作
open/close
read/write
不能执行
lseek
操作