进程间通信之匿名管道(pipe)


在这里插入图片描述

前言

管道分为匿名管道和命名管道,匿名管道只能在有共同祖先的(有亲缘关系)进程中使用,而命名管道可以在任意进程中使用,以下的“管道”指的都是匿名管道命名管道的知识后续再进行更新


管道

IPC (进程间通信)有多种方式, 管道是IPC的最基本的方式。管道是“半双工”的,即是单向的。管道是FIFO(先进先出)的。
单进程中的管道:int fd[2]
1、使用文件描述符fd[1], 向管道写数据
2、使用文件描述符fd[0], 从管道读数据
在这里插入图片描述
管道的局限性:
①数据一旦被读走,便不在管道中存在,不可反复读取。
②由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
③只能在有公共祖先的进程间使用管道。


管道的创建

在这里插入图片描述
参数:
一个大小为2的fd数组
返回值:
成功:返回 0
失败:返回 -1


注意:获取两个“文件描述符”分别对应管道的读端和写端。
1、fd[0]: 是管道的读端
2、fd[1]: 是管道的写端
如果对fd[0]进行写操作,对fd[1]进行读操作,可能导致不可预期的错误。

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

int main(void) 
{
	int fd[2];
	int ret;
	ret = pipe(fd);
	....
	....
}

管道的使用

管道虽然可以在单进程中使用,但是其实没啥用处,更多地是在多进程的环境下使用,不过既然写了,也都举个例子吧!


单进程使用管道进行通信

创建管道后,获得该管道的两个文件描述符,不需要普通文件操作中的open操作
在这里插入图片描述
直接看代码吧,挺简单的👇

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

int main(void) 
{
	int fd[2];
	int ret;
	char buff1[1024];
	char buff2[1024];

	ret = pipe(fd);
	if (ret !=0) {
		printf("create pipe failed!\n");
		exit(1);
	}

	strcpy(buff1, "Hello!");
    //写数据一定是在fd[1]中写
	write(fd[1], buff1, strlen(buff1)); 
	printf("send information:%s\n", buff1);
    //将buff2清零
	bzero(buff2, sizeof(buff2));
    //读数据一定是在fd[0]中写
	read(fd[0], buff2, sizeof(buff2));
	printf("received information:%s\n", buff2);

	return 0;	
}

运行结果:
在这里插入图片描述


多进程使用管道进行通信

由于管道的通信是半双工的(数据只能由一端流到另外一端,而不能反过来发送数据),所以如果要在多进程中使用管道进行通信的话,一般是创建两个管道,这样就可以更清晰相互发送数据了(而不是说一个管道不能用来相互发送数据),采用父子进程的时候,子进程会“复制”父进程的资源,这样就有了2个fd[2]了,虽然名字一样,但是表示的不是同个fd[2],但是又可以相互通信(可以理解成虽然复制成了两个管道,但是两个管道是相通的,各个进程持有一个写端和读端)。
在这里插入图片描述

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

int main(void) 
{
	int fd[2];
	int ret;
	char buff1[1024];
	char buff2[1024];
	pid_t pd;

	ret = pipe(fd);
	if (ret !=0) {
		printf("create pipe failed!\n");
		exit(1);
	}
    //fork一个子进程
	pd = fork();
	if (pd == -1) {
		printf("fork error!\n");
		exit(1);
	} else if (pd == 0) {
        //子进程正确返回的pid会是0所以子进程执行这段代码
        //读数据
		bzero(buff2, sizeof(buff2));
		read(fd[0], buff2, sizeof(buff2));
		printf("child process(%d) received information:%s\n", getpid(), buff2);
        sleep(5);
        //写数据
        strcpy(buff1, "Hello! father!");
		write(fd[1], buff1, strlen(buff1)); 
		printf("child process(%d) send information:%s\n", getpid(), buff1);
	} else {
        //父进程返回子进程的pid。所以父进程执行这段代码
        //写数据
		strcpy(buff1, "Hello! child!");
		write(fd[1], buff1, strlen(buff1)); 
		printf("parent process(%d) send information:%s\n", getpid(), buff1);
        sleep(5);
        //读数据
        bzero(buff2, sizeof(buff2));
		read(fd[0], buff2, sizeof(buff2));
		printf("parent process(%d) received information:%s\n", getpid(), buff2);
	}
    close(fd[0]);
    close(fd[1]);
    //等待子进程执行完
	if (pd > 0) {
		wait();
	}
	
	return 0;	
}

运行结果:
在这里插入图片描述


关闭管道的读端/写端

此类示例的主要应用场景是:
1、如果不准备再向管道写入数据,则把该管道的所有写端都关闭,则,此时再对该管道read时,就会返回0,而不再阻塞该读操作。(管道的特性)
注意,这是管道的特性。
如果有多个写端口,而只关闭了一个写端,那么无数据时读操作仍将被阻塞。

2、多个进程通信的时候,我只想要单方向的数据流动

父子进程各有一个管道的读端和写端;
1、把父进程的读端(或写端)关闭;
2、把子进程的写端(或读端)关闭;
使这个“4端口”管道变成单向的“2端口”管道(虽然复制成了两个管道,但是两个管道是相通的,各个进程持有一个写端和读端,如果在两方的端口各关闭一个的话,那么就又会变成一个单方向的数据流动),如图:
在这里插入图片描述

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

int main(void) 
{
	int fd[2];
	int ret;
	char buff1[1024];
	char buff2[1024];
	pid_t pd;

	ret = pipe(fd);
	if (ret !=0) {
		printf("create pipe failed!\n");
		exit(1);
	}

	pd = fork();
	if (pd == -1) {
		printf("fork error!\n");
		exit(1);
	} else if (pd == 0) {
		//子进程关闭写端
		close(fd[1]);
		bzero(buff2, sizeof(buff2));
		read(fd[0], buff2, sizeof(buff2));
		printf("process(%d) received information:%s\n", getpid(), buff2);
	} else {
		//关闭读端
		close (fd[0]);
		strcpy(buff1, "Hello!");
		write(fd[1], buff1, strlen(buff1)); 
		printf("process(%d) send information:%s\n", getpid(), buff1);
		
		close (fd[1]);		
	}

	if (pd > 0) {
		wait();
	}
	
	return 0;	
}

运行结果:
在这里插入图片描述

总结

这篇文章到此结束,非常感谢您能看到这,这篇文章还不够详细,我也将持续更新,希望这篇文章能够对您有所帮助,当然,如果对于本文有任何的疑问也请在评论区中提出来,感谢您的观看!!
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值