Linux基础篇十二——管道

原创 2016年07月15日 20:40:42

管道是 Linux 支持的最初 Unix IPC 形式之一,我们可以使用管道使得两个进程之间进行通信。

管道通信实现的本质:控制同一文件在两个进程中仅打开读端或写端进行收发数据。

1.匿名管道

顾名思义,匿名管道是我们在进程里创建出来并且没有给其赋予指定文件名的一类管道,它可用于两个具有亲属关系的进程之间的通信。

在我们创建子进程时,子进程会创建自己的地址空间但是会复制父进程的文件描述符表,所以在子进程和父进程里我们使用同样的文件描述符指向的是同一个文件,并且,在我们打开文件时分别使用读方法和写方法打开我们找到的是同一个文件但是我们得到的描述符是不一样的。如图示



在上图里面,我们不难看出,管道文件被父子进程共享,这就说明了进程间通信的本质是共享数据。

匿名管道的特点:
·依赖于文件系统
·只能运用于有关系的进程 eg:父进程 子进程之间
**进程打开文件不进行关闭,在进程退出时文件被自动关闭
·管道的生命周期和进程相同
·管道内部提供进程间同步与互斥机制
·单向数据通信
管道的特殊状态:
·管道的写端写满之后会进行停止等待,读端类似
·管道的读端关闭,但写端没有关闭并且持续写入,此时,写端会收到异常信号并关闭
·管道的写端关闭,但读端没有关闭并且持续读取,直到管道结尾并返回0.


匿名管道的创建:

使用#include <unistd.h> int pipe(int fd[2]) 函数创建匿名管道,给出一个简单的例子

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

int main()
{
	int pipe_fd[2];
	if(pipe(pipe_fd) < 0)
	{
		perror("pipe");
		return 1;
	}
	pid_t id = fork();
	//child
	int i = 0;
	if(id == 0)
	{
		close(pipe_fd[0]);
		const char*msg = "I am child pro...\n";
		int count = 5;
		while(1)
		{
			write(pipe_fd[1],msg,strlen(msg));
			if(count -- == 0)
				break;
			printf("child write data :%d\n",++i);
		}
		//close(pipe_fd[1]);
	}
	//father
	else
	{
		close(pipe_fd[1]);
		char buf[1024];
		int count = 5;
		while(count -- > 0)
		{
			memset(buf,'\0',sizeof(buf));
			ssize_t _s = read(pipe_fd[0],buf,sizeof(buf)-1);
			if(_s >0)
				printf("client -> server: %s\n",buf);
			else if(_s == 0)
			{
				printf("read pipe EOF...\n");
				break;
			}
			else
				break;
		}
		//close(pipe_fd[0]);
		int status = 0;
		pid_t ret = waitpid(id,&status,0);
		if(ret == id)
		{
			printf("wait success...\n");
			printf("get sig: %d",status&0xff);
			printf("exit code: %d",status >> 8&0xff);
		}
	}
	return 0;

}
**我们使用status来接收程序退出的状态码,而它的低八位是退出信号,高八位是退出码

2.命名管道

与匿名管道不同,命名管道不仅可以用于具有亲属关系的进程之间的通信,还可以用于完全无关系的两个管道的通信。我们使一个进程负责创建管道文件另一个进程只需要打开该管道文件就可以在两个进程之间进行通信.

我们仍然使用一个例子来说明命名管道的通信:

client端程序

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>

int main()
{
	int fd = open("./myfifo",O_WRONLY);
	if(fd < 0)
	{
		perror("open error...");
		return 1;
	}
	char buf[1024];
	while(1)
	{
		memset(buf,'\0',sizeof(buf));
		printf("Please enter# ");
		fflush(stdout);
		ssize_t _s = read(1,buf,sizeof(buf));
		if(_s > 0)
		{
			buf[_s-1]='\0';
			write(fd,buf,strlen(buf));
		}
	}
	close(fd);
	return 0;
}

server端程序:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
	if(mkfifo("./myfifo",S_IFIFO | 0644) < 0)
	{
		perror("mkfifo");
		return 1;
	}

	int fd = open("./myfifo",O_RDONLY);
	if( fd < 0 )
	{
		perror("open error...");
		return 2;
	}
	char buf[1024];
	while(1)
	{
		memset(buf,'\0',sizeof(buf));
		ssize_t _s = read(fd,buf,sizeof(buf)-1);
		if(_s > 0 )
		{
			printf("Client->Server# %s\n",buf);
		}
		else
		{
			break;
		}

	}
	close(fd);
	return 0;
}

我们让客户端进行数据的写入让服务端进行数据的读出,始终保持整个通信处于半双工的状态,并且,谁创建谁回收,我们让服务端自己回收关闭我们的管道文件。这样我们就完成了两个毫无关系的进程之间的通信。

一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道有容量,它的容量大小存储于一个叫做PIPE_BUF的字段里,它规定了管道缓冲区的大小。其位于 include/linux/limits.h中,我们可以自行查看。不同的内核版本可能会有所不同。Posix.1 要求 PIPE_BUF 至少为 512 字节,red hat 7.2 中为 4096。

管道的原理:管道实际上是在内核创建了一个inode和一个指向它的固定大小的内核缓冲区,读写的文件描述符不同但指向了同一块内核和缓冲区。管道pipe是一类特殊的文件pipefs,在磁盘里没有映像,只存在于内存中,这样设计的原因是处于对管道文件读写速度的优化。

如果我们需要实现全双工的通信方式就在两个进程之间创建两个管道,一个正向通信另一个反之。


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Linux基础篇 进程通信——管道

IPC(InterProcess Communication)进程间通信 每个进程各⾃自有不同的⽤用户地址空间,任何⼀一个进程的全局变量在另⼀一个进程中都看不到所以进 程之间要交换数...

(续)linux程序设计之c基础教程——管道

本节补充管道相关的知识,但是本节课不属于原来的(linux程序设计之c基础教程)当中的知识点! 这里另作补充: linux 管道 管道是Linux中很重要的一种通信方式,是把一个程序的输...

opencv学习笔记(十二)——基础应用

这一节主要实现opencv的一些基础应用,如创建滑动条,以及控制鼠标操作等。 一、滑块条的创建 滑块条常用来动态调节参数。它需要依附窗口而存在。 1,函数介绍: 创建滑块条:creat...

黑马程序员——基础学习(十二)异常(Throwable)类、文件(File)类及递归

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 异常类(Throwable) 异常:程序在运行的过程中出现了不正常的情况。 异常的分类: Th...

Objective-C(十二、快速枚举,枚举器NSEnumerator和集合类NSSet)——iOS开发基础

版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/zsk_zane 结合之前的学习笔记以及参考《Objective-C编程全解(第三版)》,对...

Objective-C(十二、快速枚举,枚举器NSEnumerator和集合类NSSet)——iOS开发基础

快速枚举,枚举器NSEnumerator和集合类NSSet的介绍和常用方法

Java线程和多线程(十二)——线程池基础

Java 线程池管理多个工作线程,其中包含了一个队列,包含着所有等待被执行的任务。开发者可以通过使用ThreadPoolExecutor来在Java中创建线程池。 线程池是Java中多线程的一个重...

JAVA基础再回首(十二)——Character类、Math类、Random类、System类

JAVA基础再回首(十二)——Character类、Math类、Random类、System类 版权声明:转载必须注明本文转自程序员杜鹏程的博客:http://blog.csdn.net/m366...

黑马程序员——Java基础(十二)之网络编程

-----------android培训、java培训、java学习型技术博客、期待与您交流!------------ 网络编程概述: 是用来实现网络中不同计算机上运行的程序间可以进行数据交换
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)