管道(一)——匿名管道、pipe函数——linux系统编程

进程间通讯IPC

上一讲我们说到信号,进程间的整型数据可以通过信号进行传递,但是其他信号(例如结构体)信号就不够用了。而在进程中我们学到每个进程都有各自独立的地址空间,进程互相不能访问,所以进程间若想进行数据交换,就必须通过内核。在内核中开辟一块缓冲区,用于传递信号的写入和读出,这种机制称为进程间通信(IPC,InterProcess Communication)

什么是管道、管道的特点

管道是Linux支持的最初Unix IPC形式之一,具有以下特点:

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
  • 通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道的实现机制:
管道是由内核管理的一个缓冲区。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

管道的创建——pipe函数

头文件<unistd.h>
功能:创建一无名管道
原型:int pipe(int file_descriptor[2]);
参数:
file_descriptor:文件描述符数组,其中file_descriptor[0]表示读端,file_descriptor[1]表示写端
返回值:
成功返回0,失败返回错误代码
创建成功后,在两个进程中,使用read函数和write函数进行读写操作。
特别注意read函数和write函数的第二个参数是void *
所以如果想用匿名管道传送结构体,记得取地址哦
这是在代码中使用管道的函数,另外一种在shell中使用管道,用“|”即可,这里不赘述。

代码实验

我们以父进程写hello rabbit!,子进程读出并打印hello rabbit!为例:

#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>

using namespace std;


int main(int argc, char *argv[])
{
	int pipe_fd[2];
	int r_size = 0;
	
	pid_t child_pid;
	if (pipe(pipe_fd) == -1)
	{
		perror("pipe error:");
		return -1;
	}

	
	//那么这里pipe就会打开2个文件描述符pipe_fd[0]表示读端
	child_pid = fork();
	if (child_pid == 0)//读
	{
		//子进程也继承了2个文件描述符
		close(pipe_fd[1]);//子进程读端我们会关闭写端
		char recv_buffer[50];
		memset(recv_buffer, 0, sizeof(recv_buffer));
		read(pipe_fd[0], recv_buffer, sizeof(recv_buffer));//希望读到buffer的所有字节
		cout << "child recv buffer=" << recv_buffer << endl;
		
	
		while (1)
		{
			sleep(1);//不让子进程结束
		}
	}
	else if (child_pid > 0)//写
	{		
		
		close(pipe_fd[0]);//父进程写端我们会关闭读端
		char buffer[50] = { 0 };
		strcpy(buffer, "hello rabbit!");
		//如果发送的是字符串那么可以使用strlen函数
		r_size = write(pipe_fd[1], buffer, strlen(buffer));
		
		while (1)
		{
			sleep(1);//不让父进程结束
		}
	}
	return 0;
}

打印结果:
在这里插入图片描述

代码不难,只需要注意一个点:
匿名管道是与fork互相依存的,也就是说pipe产生读写2个文件描述符后,通过fork复制到子进程,所以实际上父子各有2个文件描述符,一共是4个描述符,而管道是单向的,所以一个进程只会用到读/写一端,通常我们将另外一端close掉。
也就是说,一个进程若想又读又写,必须创建两条管道才可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值