热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【UNIX网络编程】进程间通信之管道

管道是最早的Unix进程间通信形式,它存在于全部的Unix实现中。关于管道,有例如以下几点须要知道:1、它是半双工的,即数据仅仅能在一个方向上流动。虽然在某些Unix实现中管道能

      管道是最早的Unix进程间通信形式,它存在于全部的Unix实现中。关于管道,有例如以下几点须要知道:

1、它是半双工的,即数据仅仅能在一个方向上流动。虽然在某些Unix实现中管道能够是全双工的。但须要对系统进行某些设置。在Linux系统中,它是半双工的。

2、它没有名字。因此仅仅能在具有公共祖先的进程之间使用。

通经常使用在父子进程间。虽然这一点随着“有名管道FIFO”的增加得到改正了。但应该把它们看作是两种不同的进程间通信方式。

3、它由pipe函数创建,read和write函数訪问,提供单向数据流。除了pipe外。在C函数库里,还有另外一个函数popen完毕一个新管道的创建、一个新进程的启动、关闭管道的不使用端、运行shell命令、等待命令终止等一系列操作。


       管道使用演示样例:

       在shell命令中,我们经经常使用到"cmd1 | cmd2"这一类的命令,cmd1和cmd2之间就是通过管道来进行连接的。

shell负责两个命令的标准的输入好标准输出:

cmd1的标准输入来自终端键盘。

cmd1的标准输出传递给cmd2,作为它的标准输入。

cmd2的标准输出连接到终端屏幕。


知识点1:pipe函数

#include 
int pipe(int fd[2]);			//返回值:若成功则返回0,若出错则返回-1

       进程调用pipe函数创建一个管道。pipe函数的參数是一个由两个整数类型的文件描写叙述符组成的数组的指针。该函数在数组中成功填入两个新的文件描写叙述符后返回0,假设失败则返回-1并设置errno来表明失败的原因。

errno的值有下面三种可能:

       EMFILE:进程使用的文件描写叙述符过多。

       ENFILE:系统的文件表已满。

       EFAULT:文件描写叙述符无效。

两个新填入的文件描写叙述符:fd[0]为读而打开。fd[1]为写而打开。fd[1]的输出是fd[0]的输入。也就是说利用write函数写到fd[1]的全部数据都能够从fd[0]读出来。示比例如以下:

#include 
#include 
#include 
#include 


#define MAXLINE 2048


int
main(int argc, char **argv)
{
	int fd[2];
	int data;
	char buff[MAXLINE];
	const char some_data[] = "123";


	memset(buff, \'\0\', sizeof(buff));


	if(pipe(fd) == -1){
		exit(EXIT_FAILURE);
	}else{
		data = write(fd[1], some_data, strlen(some_data));	
		data = read(fd[0], buff, data);
		printf("read %d bytes : %s\n", data, buff);
		exit(EXIT_SUCCESS);
	}
}

程序执行结果:

book@book-desktop:/work/tmp/unp$ ./a.out 
read 3 bytes : 123

上面的样例是在同一进程中使用管道的样例,但实际使用过程中非常少这样使用。一般都是在两个不同进程(一般是父子进程)间进程通信的。两个进程间使用管道的演示样例:

#include 
#include 
#include 
#include 

#define MAXLINE 2048

int
main(int argc, char **argv)
{
	int fd[2];
	int data;
	pid_t child_pid;
	char buff[MAXLINE];
	const char some_data[] = "123";

	memset(buff, \'\0\', sizeof(buff));

	if(pipe(fd) == 0){
		child_pid = fork();
		if(child_pid == -1){
			fprintf(stderr, "fork error.");	
			exit(EXIT_FAILURE);
		}else if(child_pid == 0){
			close(fd[1]);  //关闭子进程的写入端
			data = read(fd[0], buff, MAXLINE);//从子进程的读取段读取数据	
			printf("read %d bytes: %s\n", data, buff);
			exit(EXIT_SUCCESS);
		}else{
			close(fd[0]);//关闭父进程的读取段
			data = write(fd[1], some_data, strlen(some_data));//从父进程的写入端写入数据
			printf("wrote %d bytes\n", data);
		}	
	}	
	exit(EXIT_SUCCESS);
}

程序执行结果是:

book@book-desktop:/work/tmp/unp$ ./a.out 
wrote 3 bytes
read 3 bytes: 123

book@book-desktop:/work/tmp/unp$ ./a.out 
wrote 3 bytes
book@book-desktop:/work/tmp/unp$ read 3 bytes: 123
这是由于。假设父进程先于子进程结束。就会看到shell提示符了。由上面的演示样例能够看出,要通过管道完毕父子进程间的通信,先由父进程创建一个管道后调用fork派生一个自身的副本,接着,父进程关闭这个管道的读出端。子进程关闭同一管道的写入端。这就在父子进程间提供了一个单向数据流。

       当管道的一端被关闭后,下列两条规则起作用:

1、当读一个写端已被关闭的管道时,在全部数据都被读取后,read返回0,以指示达到了文件结束处。

2、假设写一个读端已被关闭的管道,则产生信号SIGPIPE。

假设忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE。


上面的两个样例都是半双工的即单向的,仅仅提供一个方向的数据流。

当须要一个双向数据流时,必须创建两个管道。每一个方向一个。实际过程例如以下:

1、创建管道1和管道2

2、fork

3、父进程关闭管道1的读出端、关闭管道2的写入端

3、子进程关闭管道1的写入端、关闭管道2的读出端。

创建两个管道后就能够完毕一个简单的client-server样例。相关演示样例请点此链接。


知识点2:popen和pclose函数

       popen和pclose函数不是Unix实现的。它们时标准I/O库提供的,它们实现的操作时:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端。执行一个shell以执行命令,然后等待命令终止。

#include 
FILE *popen(const char *cmdstring, const char *type);//返回值:若成功则返回文件指针,若出错则返回NULL
int     pclose(FILE *fp);//返回值:cmdstring的终止状态,若出错则返回-1
函数popen先运行fork,然后调用exec以运行cmdstring,而且返回一个标准I/O文件指针。

假设type是“r”,则文件指针连接到cmdstring的标准输出。返回的文件指针时可读的。
假设type是“w”,则文件指针连接到cmdstring的标准输入,返回的文件指针时可写的。
图示popen例如以下:


假设type是"r",被调用程序的输出就能够由调用程序使用,调用程序利用popen函数返回的文件指针,就能够通过经常使用的stdio库函数(如fread)来读取被调用程序的输出;假设type是"w",调用程序就能够用fwrite调用向被调用程序发送数据,而被调用程序能够在自己的标准输入上读取这些数据。


type是r的演示样例程序例如以下:
#include 
#include 
#include 
#include 

int
main(int argc, char **argv)
{
	FILE *read_fp;
	char buff[BUFSIZ + 1];
	int  chars_read;
	memset(buff, \'\0\', sizeof(buff));
	read_fp = popen("uname -a", "r");//打开连接到uname命令的管道。把管道设置为可读方式并让read_fp指向该命令输出
	if(read_fp != NULL){
		chars_read = fread(buff, sizeof(char), BUFSIZ, read_fp);	
		if(chars_read > 0)
				printf("Output was : -\n%s\n", buff);
		pclose(read_fp);
		exit(EXIT_SUCCESS);
	}
	exit(EXIT_SUCCESS);
}
执行结果:
Output was : -
Linux book-desktop 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:04:26 UTC 2009 i686 GNU/Linux
type是w的演示样例程序例如以下:
#include 
#include 
#include 
#include 

int
main(int argc, char **argv)
{
	FILE *fp;
	char buffer[BUFSIZ + 1];

	sprintf(buffer, "Once upon a time there was ...\n");
	fp = popen("od -c", "w");
	if(fp != NULL){
		fwrite(buffer, sizeof(char), strlen(buffer), fp);	
		pclose(fp);
		exit(EXIT_SUCCESS);
	}
	exit(EXIT_SUCCESS);
}
程序结果例如以下:
0000000   O   n   c   e       u   p   o   n       a       t   i   m   e
0000020       t   h   e   r   e       w   a   s       .   .   .  \n
0000037
也能够上上面的pipe函数一样实现client-server程序。演示样例代码请点此链接。

參考:
1、上面解说的图示部分:http://blog.csdn.net/to_be_it_1/article/details/28138063
2、《Linux程序设计》 Neil Matthew&&Richard Stones
3、《UNIX环境高级编程》Richard Stevenson
4、《UNIX网络编程 卷2》 Richard Stevenson






推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
author-avatar
手机用户2502858127
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有