Linux之进程间通信②——管道(pipe、fifo)

在六种进程间通信方式中,管道是最简单、效率最差的一种,管道属于半双工通信,即可以实现双向通信,但不能在两个方向上同时进行,必须交替进行

管道类型分为两种:匿名管道和命名管道
匿名管道:指不带名字标识的管道,用于父进程与其子进程之间的通信(即具有亲缘关系),常直接称匿名管道为管道
命名管道:指带有名字标识符的管道,支持任意两个进程之间的通信

一、管道(pipe)

1.基本介绍

管道的实质是内核缓冲区,进程以先进先出(FIFO,First In First Out)的方式从缓冲区存取数据:管道的一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或满的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。
局限性:
①半双工,不支持同时双向
②只支持具有亲缘关系的进程之间
③一个进程不能同时操作读、写两端
④数据为一次性可读,读后便不存在管道中,不能重复读取

2.函数介绍

(1)pipe()

#include <unistd.h>
int pipe(int fd[2]);

①函数功能:创建管道
②函数参数:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出就是fd[0]的输入
③函数返回值:成功,返回0;失败,返回-1;

3.管道编程

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

#define	MSG_STR	    "This message is from parent:Hello,child process!"

int main(int argc,char  **argv)
{
    
    
	int	pipe_fd[2];				//定义读写文件描述符
	int	rv;
	int	wstatus;				//保存wait()函数返回的子进程状态
	char	buf[1024];
	pid_t	pid;				//定义进程号
	if(pipe(pipe_fd) < 0)		//先创建管道在创建进程
	{
    
    
		printf("create pipe failure:%s\n",strerror(errno));
		return -1;
	}
	printf("create pipe successsfully!\n");

	if((pid = fork()) < 0)		//创建进程
	{
    
    
		printf("create child process failure:%s\n",strerror(errno));
		return -1;
	}
	else if (pid == 0)			//子进程运行
	{
    
    
		close(pipe_fd[1]);		//子进程关闭写通道
		memset(buf,0,sizeof(buf));
		rv = read(pipe_fd[0],buf,sizeof(buf));		//从读端读取管道内容
		if( rv < 0 )
		{
    
    
			printf("child process read data from pipe failure:%s\n",strerror(errno));
			return -1;
		}
		printf("child process read %d bytes from pipe:%s\n",rv,buf);
		return 0;
	}
	close(pipe_fd[0]);		//父进程关闭读通道
	if(write(pipe_fd[1],MSG_STR,sizeof(MSG_STR)) < 0)	//从写端往管道写内容
	{
    
    
		printf("parent process write data to pipe failure:%s\n",strerror(errno));
		return -1;
	}

	printf("parent process start waiting child process exit...\n");
	wait(&wstatus);//等待子进程终止并回收
	return 0;
}


二、命名管道(fifo)

1.基本介绍

匿名管道仅仅在具有亲缘关系的进程中使用,而命名管道可以在任意进程中使用,使用范围更为广。命名管道是借助文件系统实现的,文件系统中的路径名是全局的,各进程都可以访问。

2.函数介绍

(1)access()

#include <unistd.h>
int access(const char*pathname,int mode);

①函数功能:检查是否可以对某文件进行某种操作
②函数参数:pathname指文件路径名+文件名,mode在指定access的作用下,有如下值

F_OK 值为0,判断文件是否存在
X_OK 值为1,判断对文件是可执行权限
W_OK 值为2,判断对文件是否有写权限
R_OK值为4,判断对文件是否有读权限
注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK

③函数返回值:成功,返回0 ;失败则返回-1;

(2)mkfifo()

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);

①函数功能:使用指定的文件名创建FIFO(命名管道)
②函数参数:pathname指文件名,mode为该文件的权限;
③函数返回值:成功,返回0;失败,返回-1;

(3)fgets()

#include <stdio.h>
char *fgets(char *buf,int size,FILE *stream);

①函数功能:从标准流中获取字符串,当遇到‘\0’或读取字符等于(size-1)就会停止;
②函数参数:buf为存储字符串的位置;size为存储字符串的最大个数(size-1);stream指向读取的流,使用中一般写为STDIN,意为标准输入流;
③函数返回值:成功,返回字符串;错误或文件结束条件返回NULL;可以使用feof或ferror来确定是否发生错误

扫描二维码关注公众号,回复: 15347787 查看本文章

3.fifo编程

创建两个掩藏的命名管道文件在不同的进程间进行双向通信,最后形成两个类似聊天窗口

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h>
#include <stdlib.h>

#define FIFO_FILE1		".fifo_chat1"
#define FIFO_FILE2		".fifo_chat2"

int	g_stop = 0;

void sig_pipe(int signum)
{
    
    
	if(SIGPIPE == signum)
	{
    
    
		printf("get pipe broken signal and let programe exit\n");
		g_stop = 1;
	}
}

int main(int argc,char **argv)
{
    
    
	int	fdr_fifo;
	int	fdw_fifo;
	int	rv;
	int	mode = 0;
	char	buf[1024];
	fd_set	rdset;

	if(argc != 2)	//通道选择
	{
    
    
		printf("please select a fifo to chat(fifo_chat 0 or 1)\n");
		printf("This chat program need run twice,1st time run with [0] and 2nd run with [1]\n");
		return -1;
	} 
	mode = atoi(argv[1]);	//将管道选择从字符转换为整型

	if( access(FIFO_FILE1,F_OK) != 0)		//判断文件是否存在(F_OK)
	{
    
    
		printf("%s not exist and create it now\n ",FIFO_FILE1);
		mkfifo(FIFO_FILE1,0666);		//创建FIFO_FILE1,打开权限
	}
	if( access(FIFO_FILE2,F_OK) != 0)		//判断文件是否存在(F_OK)
	{
    
    
		printf("%s not exist and create it now\n ",FIFO_FILE2);
		mkfifo(FIFO_FILE2,0666);		//创建FIFO_FILE2,打开权限
	}
	signal(SIGPIPE,sig_pipe);

	if( 0 == mode)
	{
    
    
		printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",FIFO_FILE1);
		if( (fdr_fifo=open(FIFO_FILE1, O_RDONLY)) < 0 )		//判断FIFO_FILE1是否可读
 		{
    
    
 			printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
 			return -1	;
		}
		printf("start open '%s' for write...\n", FIFO_FILE2);		//FIFO_FILE2打开写通道开始写内容
 		if( (fdw_fifo=open(FIFO_FILE2, O_WRONLY)) < 0 )	//判断FIFO_FILE1是否可写
 		{
    
    
			printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
 			return -1;
		}
	}
	else
	{
    
    
		printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",FIFO_FILE1);
		if( (fdw_fifo=open(FIFO_FILE1, O_WRONLY)) < 0 )		
 		{
    
    
 			printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
 			return -1	;
		}
		printf("start open '%s' for read...\n", FIFO_FILE2);		//FIFO_FILE1打开读通道开始读内容
 		if( (fdr_fifo=open(FIFO_FILE2, O_RDONLY)) < 0 )	//判断FIFO_FILE2是否可读
 		{
    
    
			printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
 			return -1;
		}
	}

	printf("start  chating with another program now,please input message now:\n");
	while(!g_stop)
	{
    
    
		FD_ZERO(&rdset);		//清空集合
		FD_SET(STDIN_FILENO,&rdset);	//将给定的描述符(标准输入)加入集合
		FD_SET(fdr_fifo,&rdset);		

		rv = select(fdr_fifo+1,&rdset,NULL,NULL,NULL);
		if( rv <= 0)
		{
    
    
			printf("select  get  timeout  or error:%s\n",strerror(errno));
			continue;
		}
		if(FD_ISSET(fdr_fifo,&rdset))		//判断指定描述符是否在集合中
		{
    
    
			memset(buf,0,sizeof(buf));
			rv = read(fdr_fifo,buf,sizeof(buf));
			if(rv < 0)
			{
    
    
				printf("read data from FIFO failure:%s\n",strerror(errno));
				break;
			}
			else if(0 == rv)
			{
    
    
				printf("another side of FIFO get closed and progarm will exit now\n");
				break;
			}
			printf("--%s",buf);
		}
		if(FD_ISSET(STDIN_FILENO,&rdset))
		{
    
    
			memset(buf,0,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);	//从标准输入读取整行输入
			write(fdw_fifo,buf,strlen(buf));
		}
	}
}

最终实现效果:

./fifo_chat 0
start open '.fifo_chat1' for read and it will blocked untill write endpoint opened...
start open '.fifo_chat2' for write...
start  chating with another program now,please input message now:
read 6 word--hello
how are you
read 19 word--i'm fine,thank you

./fifo_chat 1
start open '.fifo_chat1' for write and it will blocked untill read endpoint opened...
start open '.fifo_chat2' for read...
start  chating with another program now,please input message now:
hello
read 12 word--how are you
i'm fine,thank you
another side of FIFO get closed and progarm will exit now

猜你喜欢

转载自blog.csdn.net/xll102500/article/details/129441666