Linux进程间通信(IPC) ---- 管道(pipe)

本文深入解析管道通信机制,包括shell命令下的管道使用、进程管道的工作原理、有名管道与无名管道的区别,以及如何使用write和read函数实现简单的管道通信。探讨了管道在进程间通信中的作用,以及在关闭管道一端时的处理方式。

   

目录

管道

1、什么是管道?

2、shell命令下的管道

3、进程管道

4、pipe函数

5、有名管道和无名管道的区别

6、当管道的一端被关闭后,会发生什么样的事情?

7、使用write、read函数实现简单的管道通信


   进程间通信就是在进程间交换信息,之前的方法只能是由fork或exec传送打开文件,或者通过文件系统。但是在UNIX系统和Linux系统中还有别的方式可以进行进程间通信(Inter Process Communication)。

进程间通信(IPC)的方式有:

  • 管道
  • 信号量
  • 共享内存
  • 消息队列
  • 套接字

管道

1、什么是管道?

当从一个进程连接数据流到另一个进程时,通常是把一个进程的输出通过管道连接到另一个进程的输入

管道是父进程和子进程间通信的常用手段。管道之所以能在父子进程间传递数据,利用的是fork调用之后两个管道文件描述符都保持打开。

2、shell命令下的管道

shell命令创建管道文件,通常使用命令:mkfifo + 管道名 。管道文件的大小永远为0,文件类型为p。

它的实现实际上就是把一个进程的输出直接传递给另一个进程的输入。对于shell命令来说,命令的连接时通过管道字符来完成的。比如:

cmd1 | cmd2

shell负责安排这两个命令的标准输入和标准输出。

  • cmd1的标准输入来自键盘终端
  • cmd1的标准输出传递给cmd2,作为cmd2的标准输入
  • cmd2的标准输出连接到终端屏幕

shell所做的工作实际上是对标准输入和标准输出流进行了重新连接,使数据流从键盘输入通过两个命令最终输出到屏幕上。

3、进程管道

管道是UNIX系统IPC最古老的形式,并且所有UNIX系统都提供此种通信机制。但是,管道有两种局限性:

  • 历史上,它们是半双工的(即数据只能在一个方向上流动)。
  • 它们只能在具有公共祖先的进程之间使用。
  • FIFO没有第二种局限性,UNIX域套接字和命名流管道则没有这两种局限性。

4、pipe函数

管道是由调用pipe函数而创建的,通过这个函数在两个程序之间传递数据不需要启动一个shell命令来解释请求的命令。它同时还提供了对读写数据的更多控制。

pipe函数原型如下:

#include<unistd.h>

int pipe(int pipefd[2]);

 

pipe函数的参数是一个由两个整数类型的文件描述符组成的数组的指针。该函数在数组中填上两个新的文件描述符后返回0,如果失败则返回-1并设置errno来表明失败的原因。在Linux手册页中定义了下面一些错误。

  • EMFILE:进程使用的文件描述符过多
  • ENFILE:系统的文件表已满
  • EFAULT:文件描述符无效     

两个返回的文件描述符以一种特殊的方式连接起来。写到pipefd[1]的所有数据都可以从pipefd[0]读回来。数据基于先进先出的原则进行处理。特别需要注意的是,这里使用的是文件描述符而不是文件流,所以我们必须用底层的read和write调用来访问数据,而不是使用文件流库函数fread和fwrite。

注:写到管道的数据在内存中存放。

5、有名管道和无名管道的区别

  • 有名管道在任意两个进程中使用。
  • 无名管道只能在父子进程中使用。

6、当管道的一端被关闭后,会发生什么样的事情?

  • 管道写端彻底关闭后,在所有数据都被读取后,读端read返回值为0,以指示达到了文件结束处
  • 读端关闭,写端write引发异常,异常信号为SIGIPE。如果忽略该信号或者捕捉该信号从其处理程序返回,则write返回-1,errno设置为-1。

在写管道(或FIFO)时,常量PIPE_BUF规定了内核中管道缓冲区的大小。如果对管道调用write,而且要求写的字节数小于等于PIPE_BUF,则此操作不会与其他进程对同一管道(或FIFO)的write操作穿插进行。但是若有多个进程同时写一个管道(或FIFO),而且有进程要求写的字节数超过PIPE_BUF字节数时,则写操作的数据可能相互穿插。用pathconf或fpathconf函数可以确定PIPE_BUF的值。

7、使用write、read函数实现简单的管道通信

写端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>

int main()
{
    int fd = open("./fifo",O_WRONLY);//指定操作路径和以只写的方式打开
    assert(fd != -1);

    printf("fd = %d\n",fd);//打印open返回的文件描述符

    while(1)
    {
        printf("input:\n");
        char buff[128] = {0};
        fgets(buff,128,stdin);//键盘接收
        if(strncmp("end",buff,3) == 0)//退出
        {
            break;
        }
        write(fd,buff,strlen(buff));//往fd写入数据
    }
    close(fd);//关闭文件描述符
}

读端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>

int main()
{
    int fd = open("./fifo",O_RDONLY);//指定操作路径和以只读的方式打开
    assert(fd != -1);

    printf("fd = %d\n",fd);//打印open返回的文件描述符

    while( 1 )
    {
         char buff[128] = {0};
         int n = read(fd,buff,127);//读取fd上的数据,返回值n为实际读取到的字节数
         if( n == 0)//写端关闭
         {
             break;
         }
         printf("n = %d,buff = %s \n",n,buff);
    }    
    close(fd);
}

根据运行结果可以看出来,读端和写端打印的文件描述符一致。写端写入的数据,在读端全都成功读出。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值