目录
前言
管道
匿名管道
代码实现
命名管道
相关接口
代码实现
管道符|
操作系统给用户提供了几种进程间通信的方式(管道、共享内存、消息队列、信号量)。
进程间通信的缘由是因为进程之间是相互独立的,无法直接进行通信,所以需要依赖操作系统提供相应的方式
作用:管道的作用是实现进程间的数据传输(先进先出)
本质:其本质就是内存中的一块缓冲区(内核管理的一块内存)
原理:一个进程创建一个管道(在内核中开辟一块内存),系统提供操作句柄(文件描述符)给进程,可以对管道进行操作,多个进程访问同一个管道即可实现进程间通信。
管道分为匿名管道和命名管道。
管道特性:
①半双工通信(可选方向的单方向通信)
②管道生命周期是随进程的,(如果不手动关闭,那么等所有打开管道的进程全都退出后,管道会被自动释放)
③提供字节流传输服务(数据先进先出,按顺序到达,不会丢失数据,如果面向链接的所有读端被关闭,继续写入就会异常;如果所有写端被关闭,那么继续读的话,等将管道内剩下数据全部读完后就退出,直接返回0)
④自带同步与互斥
互斥:通过同一时间的唯一访问来保证安全性,管道的读写操作在不超过PIPE_BUF(默认为4096字节)时,会保证原子性(不可分割特性,也就是说即使我们只读写了1字节数据,那么系统也会按最小4096来分配内存),原子操作就是保证一个操作不会被打断。
同步:通过对资源的访问进行一些条件限制,让进程对资源的访问更加合理,管道中没有数据,则read读的时候会阻塞,管道满了,则write会阻塞。
该类管道没有标识符,只能用于有亲缘关系的进程间通信(因为需要子进程复制父进程的相关操作句柄来获得访问该管道的能力)。由于该类管道没有名字(标识符),所以如果不是具有亲缘关系的进程,是无法找到该管道的。
操作:
int pipe(int fd[2])
功能:创建一个管道,并通过函数中的fd参数返回管道的操作句柄,其中fd[0]是从管道中读取数据的操作,fd[1]是写入。
返回值:成功返回0,失败-1。
注意:创建管道一定要在创建子进程之前,这样子进程才能复制到父进程的操作句柄。
#include
#include
#include
#include
int main(){ //匿名管道
pid_t pid;
char *s="I am father!";
int size=strlen(s);
int fd[2];
char buf[20]={0};
if(pipe(fd)==-1){ //匿名管道创建失败
printf("Pipe create failed!\n");
exit(-1);
}else{ //管道创建成功
pid=fork(); //创建子进程
if(pid<0){
printf("child process create failed!\n");
exit(-1);
}else if(pid==0){ //子进程读
printf("here is child!\n");
read(fd[0],buf,size);
printf("data in pipe: %s\n",buf); //子进程来读取父进程写入的数据
close(fd[1]); //操作完后关闭管道
close(fd[0]);
}else{ //父进程写
printf("here is father!\n");
if(write(fd[1],s,size)>0) //如果父进程写入成功
printf("father write success!\n");
close(fd[0]); //记得父进程里的管道也要关闭
close(fd[1]);
}
}
内核中具有标识符的缓冲区,多个进程通过同一个标识符来访问同一个管道。
标识符是一个可见于文件系统的管道文件
操作:mkfifo命令,mkfifo函数
原理:管道文件的创建并不涉及缓冲区的创建,因为管道文件作为标识符是一直存在的,但这个管道有没有被其他进程用于通信就不一定了。所以如果管道一直没有用来通信,那开辟缓冲区就是浪费内存了。
int mkfifo(char* pathname, mode_t mode)
其中pathname是管道文件名称,mode是管道文件访问权限
返回值:成功返回0,失败-1
命名管道特性:
①:如果以只写的方式打开文件则会阻塞,直到管道被任意一个进程以读的方式打开
②:如果以只读方式打开文件也会阻塞,直到管道被任意进程以写的方式打开。
#include
#include
#include
#include
#include
#include
#include
#include
int main(){
umask(0); //先将当前进程文件权限设置为0
int ret=mkfifo("./pipe.fifo",0644);
if(ret<0 && errno != EEXIST){//errno是个全局变量,这里表示这个名字的管道不存在
perror("open error:");
exit(-1);
}
int fd=open("./pipe.fifo",O_WRONLY);//这里也可以换成只读
if(fd<0){
perror("open error:");
exit(-1);
}
while(1){ //一直写入/读取
fflush(stdout); //将缓冲区的数据清空(写入/读出文件)
char *s="hellow";
int size=strlen(s);
int ret2 = write(fd,s,size);
if(ret2<0){
perror("write error:");
close(fd);
exit(-1);
}
}
close(fd);
return 0;
}
管道符就是用的管道的思路,将前一个指令的输出作为后一个指令的输入,管道符其实就是创建两个进程运行两个命令,也就属于是两个进程间的通信了。