均为整理的资料,侵删.
IPC的通信方式-管道.管道一般分为匿名管道和有名管道.
管道是最基本的进程通信的方式.
匿名管道特点只能在父子进程之间进行使用.
有名管道客户克服管道没有名字的限制.因此,除了具有管道所具有的功能之外,还允许无亲缘关系进程之间的通信.
匿名管道(pipe)
特征:
- 只提供单向通信,也就是说,两个进程都访问这个文件.如果进程1往文件内写东西,进程2只能读取文件的内容.
- 只能用于具有血缘关系的进程间通信.通常是父子进程的通信
- 管道是基于字节流进行通信的.
- 依赖文件系统,生命周期随着进程的结束而结束.
- 本身带有同步互斥的效果
要实现管道,我们首先介绍两个函数:
1.创建管道
调用 int pipe(int pipefd[2])
-
调用pipe函数的时候在内核中开辟了一块缓冲区用于通信,有一个读端一个写端,然后传出用户程序两个文件描述符,pipefd[0]指向管道的读端,pipefd[1]指向管道的写端(我们可以从0是标准输入,1是标准输出来记).所以管道在用户程序看起来就像一个打开的文件,我们可以通过调用read() 或者write() 向文件中读写数据.
-
返回值 如果成功返回0,失败返回-1
-
管道只能用于具有血缘关系的进程间通信,所以只有和fork之后产生的子进程进行交互.
实现通信的步骤如下:
- 调用pipe函数,由父进程创建管道,得到两个文件描述符指向管道的两端.
- 父进程调用fork创建子进程,对于子进程,也有两个文件描述符指向管道的两端.
- 父进程关闭读段,只进行写操作;子进程关闭写端,只进行读操作.管道用唤醒队列实现,数据从写端到读端,形成了进程间通信.(原因是匿名管道只能单项通信)
//仅作参考例子
#include<unistd.h>
#include<cstring>
#include<unistd.h>
#include<bits/stdc++.h>
int main(){
int fds[2];
if(pipe(fds) == -1){
perror("pipe");
exit(1);
}
pid_t pid = fork();
if(pid == 0){
close(fds[0]);
sleep(1);
write(fds[1],"nihao",5);
close(fds[1]);
exit(0);
}else{
close(fds[1]);
char buf[34]{};
int re = read(fds[0],buf,34);
printf("%s\n",buf);
close(fds[0]);
exit(0);
}
return 0;
}
有名管道(FIFO)
匿名管道虽然实现了进程间通信,但是有一定的局限性: 匿名管道必须是有血缘关系的进程进行通信; 第二它只能单工通信,一个进程写一个进程读.
有名管道
- 提供了一个路径名与它关联,以FIFO文件的形式存储文件系统中,能够实现任何进程的通信.但是匿名管道对于文件系统不可见的,它仅仅限于父子进程的通信.
- FIFO是一个设备文件,在文件系统中以文件名的形式存在.因此进程与创建FIFO的不存在血缘关系也可以通信,也是可以访问该路径的.
- FIFO遵循先进先出的原则,第一个进来的数据也会被第一个读走.
系统函数创建
- int mknod(const char * path,mode_t mod , dev_t dev);
- int mkfifo(const char * path, mod_t mod);
- 这两个函数都有创建一个FIFO文件,该文件在文件系统中真实存在.mknod 参数 path 为创建全路径,mod 为模式,存储权限,dev 是为设备的值,该值取决于文件创建的种类,它只是创建设备文件的时候才会使用.
- FIFO 在文件系统中作为一个特殊的文件存在,但是FIFO中的内容存放在内存中.
- 当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便后面使用.
- FIFO有名字,不相关的进程可以通过打开命名管道进行通信.
#include <sys/types.h>
#include <sys/stat.h>
#include <bits/stdc++.h>
int main(){
int ret = mkfifo("test_fifo",0666);
if(ret != 0){
perror("mkfifo");
}
return 0;
}
后续的操作,我们可以把这个命名管道当作普通文件一样进行操作: open(),write(),read(),close().
操作命名通道肯定要考虑默认情况的阻塞特性.
性质:
- 如果以只读方式打开,阻塞某个进程为写而打开FIFO
- 如果以只写方式打开, 阻塞某个进程为读而打开FIFO
- 假如FIFO里没有数据,调用read 从FIFO进行读取的时候也会阻塞,这个特点和无名管道是一样的.
- 通信过程中,如果写进程先退出,命名管道里没有数据,调用read函数从FIFO中读取不进行阻塞
- 通信过程中,读进程退出后,写进程向命名管道内写数据,写进程也会收到SIGPIPE信号退出.
- 调用write 函数向FIFO里写数据.当缓冲区满,write也会阻塞
举例
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int re = open("test_fifo",O_RDWR);
if(re < 0 ){
perror("open error file");
}
char se[100] = "He"
wirte(re,se,strlen(se));
return 0;
}
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int re = open("test_fifo",O_RDWR);
if(re < 0 ){
perror("open error file");
}
char se[100] ;
read(re,se,100);
return 0;
}