Linux--进程间通信--管道

Linux:进程间通信--命名管道:命名管道

1进程间通信

进程间通信,英文又称IPC(InterProcess Communication),因为每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户地址空间烤到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制就称为进程间通信。

2管道

2.1概念

管道是一种最基本的IPC机制,从本质上讲,管道也是一种文件。在LInux系统中,我们经常通过符号"|"来使用管道,用以连接两个多个命令。实际上,管道是进程与进程间的数据流通管道,它是的数据可以以一种“流”的形式在进程间流动。
管道的特点:
  1. 单向通信。
  2. 依赖文件系统。管道的生存周期随进程的退出,管道就被销毁(随进程)。
  3. 管道只能用于血缘关系的进程,通常用于父子进程。
  4. 管道在进行读写操作时,以数据流方式。
  5. 同步访问。

2.2管道的创建

Linux系统下,管道是通过pipe函数创建的,函数原型如下:
#include<inistd.h>
int pipe(int fileds[2]);//成功返回0,失败返回-1
其中,参数fileds[2]是一个长度为2的文件描述符数组,fileds[0]是读端,fileds[1]是写端。

2.3管道的读写

在单个进程中创建管道几乎没有意义的,因为管道存在的意义就是进行进程之间的通信。上面我们介绍了管道的特点,其中一条是,管道只能用于有血缘关系的进程,所以我们通过父子进程之间的通信演示一下管道的使用。

说明:
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端,父进程调用fork创建了子进程,子进程也有两个文件描述符指向同一管道。
因为管道是单向通信的,所以父进程关闭写端,子进程关闭读端。子进程可以往管道里写,父进程可以在管道里读,数据从写端流入,从写端流出,实现进程通信。
代码:
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
//创建管道,子进程只写,父进程只读

int main()
{
	int fds[2];
	int ret=pipe(fds);
	if(ret==-1)
	{
		printf("Create pipe error! errno code  is:%d\n",errno);
		return 1;
	}

	pid_t id=fork();
	if(id<0)
	{
		printf("fork error!");
		return 2;
	}
	else if(id==0)
	{
		//child
		close(fds[0]);//关闭读
		char* msg="I am child!";
		while(1)
		{
			write(fds[1],msg,strlen(msg)+1);
			sleep(1);
		}
	}
	else
	{
		close(fds[1]);//关闭写
		char buf[1024];
		while(1)
		{
		read(fds[0],buf,sizeof(buf));
		printf("father read :%s\n",buf);
		}
	
	}
	return 0;
}



从图中可以验证管道的单向通信,如果想实现父进程给子进程法消息,我们该怎么办?
我们可以再新建一个管道,使父进程写,子进程读,就完美解决了这个问题。

但是使用管道还有四种特殊情况要注意(以父进程读,子进程写为例):
1.如果子进程的写端的文件描述符关闭了,此时管道还有数据,此时父进程在读取完管道剩余数据后,在此read会阻塞返回0,就像读到文件尾。

#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
	int fds[2];
	int ret=pipe(fds);
	if(ret==-1)
	{
		printf("create pipe errro!\n");
		return 1;
	}
	pid_t id=fork();
	if(id<0)
	{
		printf("fork error!\n");
		return 2;
	}
	else if(id==0)
	{
		//child
		close (fds[0]);//关闭读
		int i=0;
		char* msg="I am child!";
		while(i<5)
		{

		write(fds[1],msg,strlen(msg)+1);
		sleep(1);
		++i;
		}
		close(fds[1]);//写入5次,关闭写
	}
	else
	{
		//father
		close(fds[1]);//关闭写
		char  buf[1024];
		int j=0;
		while(j<100)
		{
			memset(buf,'\0',sizeof(buf));
			int ret=read(fds[0],buf,sizeof(buf));
			printf("%s; code is:%d\n",buf,ret);
			++j;
		}
		if(waitpid(id,NULL,0)<0)
			return 3;
	}
	return 0;
}


2.如果子进程的写端没有关闭,但是也没有向管道写数据,此时父进程读取完管道的剩余数据,再次read会阻塞,直到管道中有数据读才返回。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
	int fds[2];
	int ret=pipe(fds);
	if(ret==-1)
	{
		printf("create pipe errro!\n");
		return 1;
	}
	pid_t id=fork();
	if(id<0)
	{
		printf("fork error!\n");
		return 2;
	}
	else if(id==0)
	{
		//child
		close (fds[0]);//关闭读
		int i=0;
		char* msg="I am child!";
		while(1)
		{
			if(i<3)
			{
				write(fds[1],msg,strlen(msg)+1);
			}

			sleep(1);
		}
	//	close(fds[1]);//写入5次,关闭写
	}
	else
	{
		//father
		close(fds[1]);//关闭写
		char  buf[1024];
		int j=0;
		while(j<20)
		{
			memset(buf,'\0',sizeof(buf));
			int ret=read(fds[0],buf,sizeof(buf));
			printf("%s; code is:%d\n",buf,ret);
			++j;
		}
		if(waitpid(id,NULL,0)<0)
			return 3;
	}
	return 0;
}


3.如果父进程的读端关闭了,这时子进程向管道写,那么进程会收到SIGPIPE,通常会导致程序异常终止。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
	int fds[2];
	int ret=pipe(fds);
	if(ret==-1)
	{
		printf("create pipe errro!\n");
		return 1;
	}
	pid_t id=fork();
	if(id<0)
	{
		printf("fork error!\n");
		return 2;
	}
	else if(id==0)
	{
		//child
		close (fds[0]);//关闭读
		int i=0;
		char* msg="I am child!";
		while(1)
		{
			if(i<10)
			{
				write(fds[1],msg,strlen(msg)+1);
			}

			sleep(1);
			++i;
		}
	//	close(fds[1]);//写入5次,关闭写
	}
	else
	{
		//father
		close(fds[1]);//关闭写
		char  buf[1024];
		int j=0;
		while(j<5)
		{
			memset(buf,'\0',sizeof(buf));
			int ret=read(fds[0],buf,sizeof(buf));
			printf("%s; code is:%d\n",buf,ret);
			++j;
		}
		close(fds[0]);
		sleep(10);//睡眠10秒,终止
		if(waitpid(id,NULL,0)<0)
			return 3;
	}
	return 0;
}


4.父进程的读端口没关闭,但是父进程也没有从管道里读文件,当子进程向管道写数据时,管道被写满时,再次write会阻塞,直到管道有空位置才写入数据并返回。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
	int fds[2];
	int ret=pipe(fds);
	if(ret==-1)
	{
		printf("create pipe errro!\n");
		return 1;
	}
	pid_t id=fork();
	if(id<0)
	{
		printf("fork error!\n");
		return 2;
	}
	else if(id==0)
	{
		//child
		close (fds[0]);//关闭读
		int i=0;
		char* msg="I am child!";
		while(1)
		{
		
			write(fds[1],msg,strlen(msg)+1);

			printf("%s; code is:%d\n",msg,i);
		//	sleep(1);
			++i;
		}
	}
	else
	{
		//father
		close(fds[1]);//关闭写
	}
	return 0;
}















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值