无名管道

 

说明:只供学习交流,转载请注明出处

 

一,管道的基本概念

管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。

无名管道又被称为pipe,是Linux所支持的IPC方式的一种。它具有如下特点:

1):只能用于血缘关系的进程之间通信。

2):属于半双工的通信模式(进程在发送数据的同时不能接收到从管道传过来的数据)。具有固定的读端和写端。

3):是一种特殊的文件,存在内存中。可通过readwrite对其进程操作。

 

说明:全双工指的的在发送数据的同时,还可以接收数据。在某些版本的UNIX中,管道是全双工的。但在Linux中,管道是半双工模式的。

 

二,无名管道的用法

因为pipe存在内存中,所以无法像操作普通文件那样通过制定路径来打开文件。通常的做法是在父进程中创建管道,在创建子进程。由于子进程继承了父进程打开的文件描述符,所以父子进程就可以通过创建的管道进程通信。

为了在子父进程中创建管道,需要先定义一个包含两个元素的整形数组,用来存放管道的读端和写端对应的文件描述符。该数组在创建管道时作为参数传递。要注意的是,管道是一种半双工通信方式,即对进程来说,要么只能读管道,要么只能写管道。不充许对管道又读又写。其中数组的第一个元素固定代表管道的读端,第二个元素代表管道的写端。对于一个进程来说,只会用到其中的一个。

数据被一个进程读出后,将被从管道中删除,其它读进程将不能再读到这些数据。管道提供了简单的流控制机制,进试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞。

 

注意:必须在系统调用fork()之前调用pipe()否则子进程将不会继承文件描述符。

 

三,管道数据的读出和写入

一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。在读写时,为了保证数据流向需要关闭一个进程的读端和另一个进程的写端。而关闭管道只需要将这两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。

 

四,pipe函数

 

头文件

#include <unistd.h>

函数原型

int pipe(int filedes[2]);

返回值

成功

失败

是否设置errno

0

-1

函数功能:创建一个无名管道。

参数说明:filedes[2]代表管道的两个文件描述符,管道创建后可以直接操作者两个文件描述符。其中filedes[0]代表读端(管道头),filedes[1]代表写端(管道尾)。

 

错误信息:

EFAULT:参数filedes非法。

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

ENFILE:进程打开的文件数达到系统上限。

 

 

 

实例:

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

int main(void)
{
	int pipe_fd[2];
	pid_t pid;
	char buf[100] = {'\0'};
	int num = 0;

	if (pipe(pipe_fd)<0)
	{
		printf("Pipe create error!\n");
		return (-1);
	}

	if ((pid=fork())==0)
	{
		printf("\n");
		close(pipe_fd[1]);//子进程关闭了写端
		sleep(2);//让父进程先运行,这样父进程先写子进程才有内容读

		if ((num=read(pipe_fd[0], buf, 100))>0)
		{
			printf("%d numbers read from the pipe is %s\n", num, buf);
		}
		close(pipe_fd[0]);
		exit(0);
	}
	else if(pid > 0)
	{
		close(pipe_fd[0]);//父进程关闭了读端
		if (write(pipe_fd[1], "hello", 5) != -1)
		
		{
			printf("Parent write1 hello!\n");
		}
		
		if (write(pipe_fd[1], " pipe", 5) != -1)
		{
			printf("Parent write2 pipe!\n");
		}
		
		close(pipe_fd[1]);
		waitpid(pid, NULL, 0);//等待子进程结束
		exit(0);

	}

	return (0);
}

运行结果:
[root@localhost test]# ./pipe 

Parent write1 hello!
Parent write2 pipe!
10 numbers read from the pipe is hello pipe
[root@localhost test]#


 

说明:我们知道无名管道的创建是在fork()前,通过pipe()创建管道(包括管道头和管道尾,分别对应读端和写端),然后通过fork()创建子进程。创建子进程后,子进程会拷贝父进程的代码段、数据段、以及堆栈段。因此fork()后,pipe()创建的管道会被复制一份。一份归父进程,一份归子进程。为了让父子进程能够通过管道正常通信,而不至于在父子进程都在向管道写或者读时,管道中的数据错乱,就必须处理已有的管道。比如在例子中,为了保证父进程写,子进程读,就要关闭父进程的读和子进程的写。这样才能保证数据流向。而且为了保证子进程能够读到数据,就要延时等到父进程已经向管道写入数据才行。

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值