Linux进程间通信之无名管道(PIPE)

本文介绍了Linux进程间通信中的无名管道PIPE,它是一个单向通信通道,适用于父子进程间通信。主要函数是pipe(),用于创建管道并提供两个文件描述符,分别对应读端和写端。父进程关闭读端,子进程关闭写端以实现数据的正确流动。文章详细解释了关闭文件描述符的原因和文件表的引用计数机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个进程各自有不同的用户地址空间,任 何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)



无名管道PIPE只适用于父子进程间的通信,还有一种叫做有名管道FIFO将会在之后作介绍.

PIPE是Linux进程间通信方法之一,它是一个单向通道,先进先出,管道的尾端为写端,描述符pipe_fd[1] . 头部为读端,描述符为pipe_fd[0](注意,强制规定)


主要函数:

int pipe(int pipe_fd[2])

调用pipe函数在内核中开辟一块缓冲区(称为管道)用于单向通信,它有一个读端一个写端,然后通过filedes参数传给用户程序两个文件描述符,filedes[0]指 向PIPE的读端,filedes[1]指向PIPE的写端。所以在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]); 向这个文件读写数据 其实是在读写内核缓冲区.

创建无名管道的基本步骤:

  1. 父进程调用pipe(int pipe_fd[2])函数,创建管道并得到两个文件描述符,分别指向管道的头部和尾部,也就是读端和写端
  2. 父进程调用fork()函数创建子进程,因为fork()函数,子进程拷贝父进程的数据段和代码段,所以子进程也能得到第一步创建的两个文件描述符,且指向同一个管道
  3. 父进程关闭管道的读端,子进程关闭管道的写端(想想为什么这么做???)
示例代码:
#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<sys/wait.h> 
#include<string.h> 
  
int main() 
{ 
    char buf[20]; 
    pid_t pid; 
    int pipe_fd[2];  
  
    pipe(pipe_fd); //创建管道 
    if ((pid = fork()) < 0) //fork子进程 
    { 
        perror("fork error"); 
        exit(-1); 
    } else if (pid == 0) //子进程中 
    { 
        close(pipe_fd[1]); //关闭写端 
	sleep(1);
        read(pipe_fd[0],buf,sizeof(buf)); 
        printf("%s",buf);
        close(pipe_fd[0]); //关闭读端 
        exit(0); //正常退出
    } else //父进程中 
    { 
        close(pipe_fd[0]); //关闭读端 
        write(pipe_fd[1],"hello world",strlen("hello world")); 
        close(pipe_fd[1]); //关闭写端 
        waitpid(pid,NULL,0); //等待子进程结束或中断,参数1为欲等待的子进程的标识码,参数2为
			     //子进程结束的状态值一般为NULL,参数3为等待何种子进程,为0时表示等待进程组识别码
			     //与目前进程相同的任务子进程
        exit(0); 
    } 
}

现在解释下之前创建无名管道基本步骤的第三部中的问题:
先来看一幅图:

1. 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。 
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。 
3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

注:之前一直没明白为什么在这里父进程要关闭管道读端,并且子进程要关闭管道写端,想了很久终于想通了...,原因如下:因为上面的这个程序是要模拟父进程和子进程的管道读写操作,其中父进程用于向管道中写入数据,子进程用于向管道中读取数据,因此开始要关闭父进程的读文件描述符filedes[0], 以及关闭子进程的写文件描述符filedes[1],这是为了模拟这个过程。

        然后至于为什么父进程关闭管道的读文件描述符filedes[0]后子进程还能读取管道的数据,是因为系统维护的是一个文件的文件描述符表的计数,父子进程都各自有指向相同文件的文件描述符,当关闭一个文件描述符时,相应计数减一,当这个计数减到0时,文件就被关闭,因此虽然父进程关闭了其文件描述符filedes[0],但是这个文件的文件描述符计数还没等于0,所以子进程还可以读取。也可以这么理解,父进程和子进程都有各自的文件描述符,因此虽然父进程中关闭了filedes[0],但是对子进程中的filedes[0]没有影响

文件表中的每一项都会维护一个引用计数,标识该表项被多少个文件描述符(fd)引用,在引用计数为0的时候,表项才会被删除。所以调用close(fd)关闭子进程的文件描述符,只会减少引用计数,但是不会使文件表项被清除,所以父进程依旧可以访问

        最后需要注意,在linux的pipe管道下,在写端进行写数据时,不需要关闭读端的缓冲文件(即不需要读端的文件描述符计数为0),但是在读端进行读数据时必须先关闭写端的缓冲文件(即写端的文件描述符计数为0)然后才能读取数据。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值