linux远程开发——匿名管道PIPE和命名管道FIFO

目录

一、什么是管道

二、匿名管道(PIPE)

1、匿名管道介绍

2、pipe()函数

3、父子进程通过匿名管道进行通信

(1) 需求分析

(2) 代码实现

(3) 测试结果

三、命名管道(FIFO)

1、命名管道介绍

2、创建命名管道

(1) 命令行方式

(2) 代码方式

(3) mkfifo()函数原型

3、打开命名管道文件FIFO

4、命名管道的打开规则


一、什么是管道

        管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。

        管道是半双工的,数据只能向一个方向流动。需要双方通信时,需要建立起两个管道。只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)进行通信。

        通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。也就是管道创建要在fork()前执行。

二、匿名管道(PIPE)

1、匿名管道介绍

        匿名管道也就是无名管道,通过pipe()函数可以创建一个管道,并且匿名管道只能在有亲缘关系的进程中进行通信,何为有亲缘关系?比如父进程与子进程或者两个由同一个父进程创建的子进程。

        因为匿名管道是半双工的,所以要实现两个进程互相通信,就需要创建两个管道,一个管道方向为父——>子,另一个为子——>父。

2、pipe()函数

        pipe()函数的功能是创建一无名管道,下面为pipe()的函数原型。

#include <unistd.h>  //头文件

int pipe(int pipefd[2]);

参数:

pipefd[2]:文件描述符数组,pipefd[0]表示读端,pipefd[1]表示写端。

返回值:

成功返回0,失败返回-1。

下面通过一个段代码使用pipe()函数来测试父子进程间通过匿名管道的通信。

3、父子进程通过匿名管道进行通信

(1) 需求分析

1.需要准备两个管道,父——>子,子——>父。

2.父——>子的管道,父亲要发送数据,所以要关闭读端,儿子要接收数据,所以要关闭写端。

3.子——>父的管道,父亲要接收数据,所以要关闭写端,儿子要发送数据,所以要关闭读端。

4.父和子两个进程需要通过while循环读取信息或发送信息。

(2) 代码实现

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

using namespace std;

int main()
{
	//pipe匿名管道:1、没有名字  2、是用在由同一个进程fork具有亲缘关系的子进程

	int pid = 0;
	int fatherTochild[2] = { 0 };//父到子文件描述符数组
	int childTofather[2] = { 0 };//子到父文件描述符数组
	
	//管道创建要在进程之前,管道一定是放在进程里面使用的,所以管道要在进程创建前创建
	if (pipe(fatherTochild) < 0 || pipe(childTofather) < 0)
	{
		perror("pipe open error");//管道创建失败
	}
	else
	{
		cout << "管道开启成功" << endl;
		pid = fork();
		//子进程
		if (pid == 0)
		{
			char resbuf[255] = { 0 };
			
			close(fatherTochild[1]);//子进程  父到子 关闭写端
			
			close(childTofather[0]);//子进程  子到父  关闭读端
			while (1)
			{
				read(fatherTochild[0], resbuf, sizeof(resbuf));//接收父亲发过来的信息
				if (strlen(resbuf) != 0)
				{
					cout << "父亲对儿子说:" << resbuf << endl;
				}
				bzero(resbuf, sizeof(resbuf));//清空数组

				cout << "儿子:";
				fgets(resbuf, sizeof(resbuf), stdin);//控制台输入
				write(childTofather[1], resbuf, sizeof(resbuf));//给父亲发信息
				bzero(resbuf, sizeof(resbuf));//清空数组
			}
		}
		else if (pid > 0)//父进程
		{
			char sendbuf[255] = { 0 };
			close(fatherTochild[0]);//父进程  父到子 关闭读端
			
			close(childTofather[1]);//父进程  子到父 关闭写端
			while (1)
			{
				cout << "父亲:";
				fgets(sendbuf, sizeof(sendbuf), stdin);//控制台输入
				write(fatherTochild[1], sendbuf, sizeof(sendbuf));//给孩子发信息
				bzero(sendbuf, sizeof(sendbuf));//清空数组

				read(childTofather[0], sendbuf, sizeof(sendbuf));//接收孩子发过来的信息
				if (strlen(sendbuf) != 0)
				{
					cout << "儿子对父亲说:" << sendbuf << endl;
				}
				bzero(sendbuf, sizeof(sendbuf));//清空数组
			}
		}
	}
	
	return 0;
}

(3) 测试结果

        通过测试结果可以发现,一开始是父亲先输入信息,原因是儿子一开始是先read(),而read()函数是一个阻塞函数,当读到信息的时候才结束。儿子收到信息后,儿子就执行完read(),并接着执行write(),同时父亲发完信息后也就是执行完write(),并接着执行read(),等待儿子发信息。就这样循环反复的收发信息,就实现了父子进程间的通信。

三、命名管道(FIFO)

1、命名管道介绍

        上面的匿名管道只能实现具有亲属关系的进程间通信,如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

        命名管道是一种特殊类型的文件,它以文件的形式存在,但不是真正的文件,并且不保留数据,只是起到一个数据传输的作用。

2、创建命名管道

(1) 命令行方式

mkfifo xxx.fifo

(2) 代码方式

umask(0);
mkfifo("/root/projects/test.fifo", 0777);

(3) mkfifo()函数原型

函数功能:创建管道文件.fifo

#include <sys/types.h> //头文件
#include <sys/stat.h> //头文件

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

参数:

pathname:文件路径

mode:文件权限

返回值:

成功返回0,失败返回-1。

3、打开命名管道文件FIFO

1、FIFO和通过pipe调用创建的管道不同,它是一个有名字的文件而表示一个打开的文件描述符。

2、在对它进行读或写操作之前必须先打开它。

3、FIFO文件也要用open和close函数来打开或关闭,除了一些额外的功能外,整个操作过程与文件操作是一样的。

4、传递给open调用的是一个FIFO文件的路径名,而不是一个正常文件的路径名。

4、命名管道的打开规则

1、如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志,即只设置了O_RDONLY),反之,如果当前打开操作没有设置了非阻塞标志,即O_NONBLOCK,则返回成功

2、如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

3、通俗的说,要打开一个FIFO命名管道文件,需要一个进程以写打开,并且另一个进程要以读打开,只有满足了有读打开和有写打开,命名管道才算打开成功。就像水管一样,水管的进口和出口需要同时打开,水才能流过去。

原创不易,转载请标明出处。

对您有帮助的话可以点赞收藏+关注,会持续更新的(嘻嘻)。

猜你喜欢

转载自blog.csdn.net/wmcy123/article/details/123606929