进程间通信pipe、fifo、mmap应用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std ;
int count;
int main (void )
{
	int fd[2];
	if (pipe(fd)==-1)
	{
		perror("open pipe err");
		exit(1);
	}
	pid_t pid=fork();
	if (pid == 0)
	{	
		char buf[10];
		printf("i am child process =%d \n",getpid());
		while (1)
		{
			read(fd[0],buf,10);
			printf("recv message to parent =%s \n",buf);
			if (++count ==10)
				break;
		}
		//return 10;
		exit(10);
	}else
	{
		printf("i am parent process =%d \n",getpid());
		while (1)
		{
			sleep(2);	
			count+=1;
			printf("count = %d \n",count);
			write(fd[1],"hello c",8);
			if (count == 10)
				break;
		}
		int status;
		int pid;
		while(1){
			pid=waitpid(-1,&status,WNOHANG);//回收子进程用
			//printf("pid =%d \n",pid);
			if (pid > 0)
				break;
		}
		if (WIFEXITED(status))
			printf("child return =%d \n",WEXITSTATUS(status));

	}
	close(fd[0]);
	close(fd[1]); 
	return 0;
}

pipe一般应用于父子(血缘悬系)进程之间的通信,不能进行跨进程通信。管道是一种最基本的IPC机制,其本质是一个伪文件(实为内核缓冲区),通过fd[1]write 写数据,fd[0] read读数据。需要注意,数据只要通过管道写进去了,不管哪个进程都可以在读端取数据。为了避免混乱一般需要创建两个管道进行互相通信。

管道数据只能读取一次不能反复读取,就像队列一样。

一,匿名管道PIPE局限性

管道的主要局限性正体现在它的特点上:

  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;

如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道,是一种特殊类型的文件。

//FIFO 写端 
static char *PIPE_NAME;
#define BUF_S 1024
int main (int args , char *argv[])
{
	if (args != 2)
	{
		fputs("please set fifo name \n",stderr);
		exit(1);
	}
	PIPE_NAME =argv[1];
	printf("crete a fifo name = %s \n ",PIPE_NAME);
	//umask(0);
	if (mkfifo(PIPE_NAME,0644)==-1)//create a fifo  )
	{
		perror("mkfifo err");
		exit(1);
	}
	printf("next open this pipe \n");
	int fd = open(PIPE_NAME,O_WRONLY);
	if (fd==-1)
	{
		perror("open err");
		exit(1);
	}

	printf(" open sucess!! \n");
	char buf[BUF_S];
	int str_len ;
	while(1)
	{
		fputs("press q to quit",stdout);
		fgets(buf,BUF_S,stdin);//终端输入数据
		//strcmp check each character 
		//检查每个字符,如果字符一样返回0,如果不一样逐个字符对比大小
		if (strcmp(buf,"q\n") &&  strcmp(buf,"Q\n"))
		{
			printf("stdin = %s \n",buf);
			write(fd,buf,sizeof(buf));
		}else
			break ;
	
	}
	close(fd);
	unlink(PIPE_NAME); //文件用完后即删除。将文件的引用计数减1,当为0的时候回自动删除。
	printf("process end \n");
	return 0;
}
读端
int main (int args , char *argv[])
{
	if (args != 2)
	{
		fputs("please set fifo name \n",stderr);
		exit(1);
	}
	PIPE_NAME =argv[1];
	printf("next open fifo name = %s \n ",PIPE_NAME);
	int fd = open(PIPE_NAME,O_RDONLY);//如果写端还没有打开将会阻塞在这。
	if (fd==-1)
	{
		perror("open err");
		exit(1);
	}
	printf("open pipe %s  suscess",PIPE_NAME);
	char buf[BUF_S];
	int str_len ;
	while(1)
	{
		str_len=read(fd,buf,BUF_S);
		if (str_len == 0)//写端调用close
		{
			printf("recev length 0 \n ");
			break;
		}
		printf("receve message =%s ",buf);
	}
	printf("process end \n");
	return 0;
}

FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一量这些工作完成之后,它们具有相同的语义。man帮助说明:The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics。

有名管道比管道多了一个打开操作:open。

FIFO的打开规则:如果以读的方式打开fifo时 ,fifo写端还没有创建,会因为没有那个文件导致No such file or director错误。如果写端打开mkfifo,此时读端还没有打开,则会阻塞直到读端打开fifo。文中unlink 一定要用,如果不用文件会残留(进程通信完后这个文件没用了,变成垃圾),注意unlink的位置,一但文件的引用为0系统就会删除这个文件,如果进程open这个文件是就会报No such file or directory;

//写端
int main(int args ,char *argv[])
{
	bool no_name=false;
	if (args != 2)
		no_name=true;
	;	
	size_t length_t=sizeof(struct people);
	printf("length_t=%d \n",length_t);
	people *p_cache ;
	if (no_name)
	{//匿名的只有父子进程这样的才行(MAP_SHARED)。其他进程不能读
		printf("no file mmap \n");
		//匿名,不依赖文件 看man只能 supported on Linux only since kernel 2.4
		p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANONYMOUS,-1,0);
		//exit(0);
	}else
	{
		int fd = open (argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
		if (fd == -1)
		{
			perror("open err");
			exit(1);
		}
		//lseek(fd,length_t);
		if(ftruncate(fd,length_t)==-1)
		{
			perror("ftruncate err");
		}
		//p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
		p_cache=(people *)mmap(NULL,length_t,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0);
		close(fd);//创建了映射区后这个文件的描述符已经没用。
		//unlink(argv[1]);//创建后这个文件就没有了,读端找不到这个文件导致错误
	}
	//验证是否映射成功
	if (MAP_FAILED==p_cache||errno!=0)
	{
		perror("mmap err");
		printf("errorno = %d \n",errno);
		exit(1);
	}	
	//printf("p_cache=%d",p_cache);		
	memset(p_cache,0,length_t);
	strcpy(p_cache->name,"ahahah");
	int count;
	while(1)
	{	
		sleep(2);
		p_cache->age+=1;
		printf("name =%s,age=%d \n",p_cache->name,p_cache->age);
		if (++count==20)
			break;
	}
	//unlink(argv[1]);//用过之后如果没有文件进程使用该文件了则会自动清除
	munmap(p_cache,length_t);//记得释放映射空间
}
读端
int main (int args , char *argv[])
{
	if (args != 2)
	{
		fputs("please set fifo name \n",stderr);
		exit(1);
	}
	PIPE_NAME =argv[1];
	printf("next open fifo name = %s \n ",PIPE_NAME);
	int fd = open(PIPE_NAME,O_RDONLY);
	if (fd==-1)
	{
		perror("open err");
		exit(1);
	}
	printf("open pipe %s  suscess \n",PIPE_NAME);
	char buf[BUF_S];
	int str_len ;
	while(1)
	{
		str_len=read(fd,buf,BUF_S);
		if (str_len == 0)
		{
			printf("recev length 0 \n ");
			break;
		}
		printf("receve message =%s ",buf);
	}
	printf("process end \n");
	return 0;

}

mmap: (mmap原理请看别人博客)

使用mmap是无比注意一下事项:

1.创建映射区的过程中,隐含着一次对映射文件的读操作

2.当MAP_SHARED时,要求映射区的权限<=文件打开的权限(处于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmap的权限是对内存限制。

3.映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭

4.特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!如ftruncate(fd,20) mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。

5.munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作(指向映射区以外)

6,.文件的偏移量必须是4k(页的大小)的整数倍

7.mmap创建映射区出错概率非常高,检查返回值,建立成功后再进行后续工作

mmap父子进程通信

父子等有血缘关系的进程之间也可以通过mmap建立映射区来完成数据通信。但是相应的要在创建映射区的时候指定对应的标志位参数flags:

MAP_PRIVATE :(私有映射)父子进程各自独占映射区

MAP_SHARED :(共享映射)父子进程共享映射区

进程之间通信优劣:https://www.cnblogs.com/lincappu/p/8536431.html

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值