Linux系统编程-进程间通信-管道

80 阅读3分钟

管道

实现原理:一块内核中缓冲区,是环形队列

特性:

  1. 伪文件,因为他是内核缓冲区,所以占用的是内存,而不是磁盘空间
  2. 管道的数据只能读取一次
  3. 数据只能单向流动

局限性:

  1. 半双工
  2. 自己写,不能自己读
  3. 数据不能反复读取
  4. 只能用于有血缘关系的进程

pipe

int pipe(int fd[2])

参数:

  • fd[0]:读端
  • fd[1]:写端

返回值:

  • 成功:0
  • 失败:-1,设置errno

原理

image.png

image.png

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

void sys_err(const char* str){
        perror(str);
        exit(1);
}

int main(int argc,char* argv[]){
        int ret;
        int fd[2];
        char buf[1024];
        ret=pipe(fd);
        if(ret==-1){
                sys_err("pipe error");
        }

        pid_t pid=fork();
        if(pid>0){  //父进程写
                close(fd[0]); //父子进程在fork瞬间共享文件描述符,两者都拥有读写端,得把自己不用的去掉,才能形成单向导write(fd[1],"hello",strlen("hello"));
                close(fd[1]);
                wait(NULL); //处理僵尸进
        }else if(pid==0){  //子进程读
                sleep(1); //防止变成孤儿进程
                close(fd[1]); //关闭写端
                ret=read(fd[0],buf,sizeof(buf));
                write(STDOUT_FILENO,buf,ret); //把读的内容写在标准输出上
                close(fd[0]);
        }else if(pid<0){
                sys_err("fork error");
        }
        return 0;
}

管道的读写行为

读管道:

  1. 管道有数据:read返回实际读到的字节数
  2. 管道无数据:(1)无写端,read返回0,相当于直接读到文件尾 (2)有写端,阻塞等待

写管道:

  1. 无读端:异常终止
  2. 有读端:(1)管道已满,阻塞等待(2)管道未满,返回写出的字节数

FIFO

一般来说,管道指的是pipe(匿名管道),用于有血缘关系的进程间通信,通过父子进程的fork的复制进行端口的传递。FIFO是有名管道,一般用于无血缘关系的进程间通信,可以主动找到这个FIFO文件的端口。

创建FIFO文件

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>

int main(int argc,char* argv[]){
	int ret=mkfifo("mytestfifo",0664);
	if(ret==-1){
		perror("mkfifo error");
		exit(1);
	}
	return 0;
}

使用FIFO文件

就是按照读写文件来就是了,一个读文件,另一个写文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char* argv[]){
	int fd,i;
	char buf[4096];
	
	fd=open(argv[1],O_RDONLY);
	if(fd<0){
		perror("open error");
		exit(1);
	}
	while(1){
		int len=read(fd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,len);
		sleep(1);
	}
	close(fd);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char* argv[]){
	int fd,i;
	char buf[4096];
	
	fd=open(argv[1],O_WRONLY);
	if(fd<0){
		perror("open error");
		exit(1);
	}

	i=0;
	while(1){ //不断往管道文件内些数据
		sprintf(buf,"hello itcast %d\n",i++);
		write(fd,buf,strlen(buf));
		sleep(1);
	}
	close(fd);
}

使用文件进行进程间通信

既然管道的进程间通信原理就是文件的读写,那可以直接用文件作为中介。他们的区别就是,文件是占据磁盘空间的,要读写磁盘,速度较慢。管道是伪文件,是内存的一片缓冲区,速度较快。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
	//创建子进程
	int pid=fork();
	char buf[4096];
	int fd=open("fifo_share",O_RDWR|O_CREAT|O_TRUNC,0644);
	if(pid<0){
		perror("fork error");
		exit(1);
	}else if(pid==0){ //子进程写
		write(fd,"hello",strlen("hello"));
	}else if(pid>0){ //父进程读
		wait(NULL);
		int len=read(fd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,len);
	}
}

有血缘关系的进程共享一个文件描述符,相当于共享一个打开的文件。 无血缘关系的进程只需要打开相同的文件。