进程间通信(1)--管道pipe

学习Linux的知识中,有一个特别重要的概念叫“进程”,而要进行进程间通信时,有一个特别重要的概念就是--管道,今天,我们就来学习一下什么是管道,它能又干什么呢?

一、概念

管道:把一个进程连接到另外一个进程的一个数据流称为管道。

          (其实,我们联系现实生活,自来水管可以将我们用户和供水站连接起来,通过管道运输水流,在这里可以借助这个例子              帮助我们理解进程间通信中管道的概念)

二、匿名管道

1.原型:

int pipe(int fd[2]);
//fd为文件描述符,其中fd[0]表示读端,fd[1]表示写端

2.下面,我们来实现一段从键盘读取数据,写入管道,读取管道,写到屏幕的代码

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

int main(void)
{
	int fds[2];
	char buf[100];
	int len;
	if (pipe(fds) == -1)
		perror("make pipe"), exit(1);
	//读取数据
	while (fgets(buf, 100, stdin)){
		len = strlen(buf);
	//写入管道
		if (write(fds[1], buf, len) != len){
			perror("write to pipe");
			break;
		}
		memset(buf, 0x00, sizeof(buf));
	//读取管道
		if ((len = read(fds[0], buf, 100)) == -1){
			perror("read from pipe");
			break;
		}
	//写到屏幕
		if (write(1, buf, len) != len){
			perror("write to stdout");
			break;
		}
	}
}

3.通过父进程fork出子进程通过管道连接的过程如下:

(1)父进程创建管道


(2)父进程fork出子进程(左边父进程,右边子进程)


(3)父进程关闭fd[0],子进程关闭fd[1]


管道的读写操作是相对应的,当父进程fork出子进程时,两边对同时打开读写端,这样会导致程序不知道到底在管道哪一端读数据,哪一端写数据。因此,我们必须关闭关闭父进程的一端和子进程对应的另外一端,这样才能进行正常的读写操作。

说明:管道具有原子性,当写入管道的数据不大于pipe_buf时,linux将保证写入数据的原子性;当写入管道的数据大于pipe_buf时,linux将不保正写入数据的原子性。

4.匿名管道的特点

(1)只能用于具有共同祖先的进程(有亲缘关系)之间进行通信。通常,一个管道由一个进程创建,然年该进程调用fork,之后          父子进程就可以用该管道进行通信;

(2)管道提供流式服务;

(3)管道的生命周期随进程(一般而言,进程退出,管道释放);

(4)一般而言,内核会对管道进行同步和互斥;

(5)管道是半双工的,数据只能由一个方向流动;双方通信时,需要建立两个管道。

这里可能会有一些比较陌生的词语,我们来解释一下:

(1)把两个进程看到的公共资源叫做临界资源,这两个进程的代码叫做临界区;

(2)管道在访问临界资源时,既要保证互斥,又要保证访问的原子性;

(3)同步是指在保证数据安全时,按照某种特性进行。

三、命名管道

看了匿名管道之后,我们肯定会有疑问,具有亲缘关系的管道之间可以进行通信,那么几个不相关的进程之间是否可以进行通呢?这里我们就引进了命名管道的概念。

1.命名管道:通过FIFO文件来进行不相关进程的通信,命名管道是一种特殊类型的文件

2.命名管道的创建

(1)在命令行创建

$ mkfifo filename

(2)从程序里创建

int mkfifo(const char * filename, mode_t mode);

(3)创建

int main(int argc, char * argv[])
{
	mkfifo("p2", 0644);
	return 0;
}

四、匿名管道和命名管道的区别

1.匿名管道由pipe函数创建并打开;而命名管道由mkfifo,用open打开。

2.匿名管道(pipe)和命名管道(FIFO)最大的差别就是管道的创建和打开不同,一旦工作完成后,它们具有相同的语义。

下面,我们用命名管道来实现文件拷贝

读取文件,写入管道:

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

#define ERR_EXIT(m)\
do\
{\
	perror(m);\
	exit(EXIT_FAILURE);\
}while (0)

int main(int argc, char * argv[])
{
	mkfifo("tp", 0644);
	int infd;
	infd = open("abc", O_RDONLY);
	if (infd == -1)
		ERR_EXIT("open");
	int outfd;
	outfd = open("tp", O_WRONLY);
	if (outfd == -1)
		ERR_EXIT("open");

	char buf[1024];
	int n;
	while ((n == read(infd, buf, 1024)) > 0)
	{
		write(outfd, buf, n);
	}
	close(infd);
	close(outfd);
	return 0;
}

读取管道,写入目标文件:

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

#define ERR_EXIT(m)\
do\
{\
	perror(m); \
	exit(EXIT_FAILURE); \
}while (0)

int main(int argc, char * argv[])
{
	int outfd;
	outfd = open("abc.bak",O_WRONLY|O_TRUNC,0644);
	if (outfd == -1)
		ERR_EXIT("open");
	int infd;
	outfd = open("tp", O_RDONLY);
	if (outfd == -1)
		ERR_EXIT("open");
	char buf[1024];
	int n;
	while ((n = read(infd, buf, 1024)) > 0)
	{
		write(outfd, buf, n);
	}
	close(infd);
	close(outfd);
	unlink("tp");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值