linux---管道

管道


一. 管道

管道:一个进程来凝结到另一个进程的一个数据流称为一个”管道”。

管道实际上就是一个固定大小的缓冲区,就是一种文件。并且都只是内核当中的一段内存,管道只是一个入口,若是没有进程打开该内存,则该管道就不会创建。


管道又分为:

  1. 匿名管道
  2. 命名管道

二. 匿名管道

匿名管道是适用于有亲缘关系的进程之间的通信。
这里写图片描述
   这里的fd是一个数组。一个有两个元素的数组,一个是fd[0]是管道的读端,fd[1]是管道的写端。

  • 写:是先将要传送的数据写入到缓冲区中,再将缓冲区的内容写入到管道之中。
  • 读:是将管道当中的数据先读到缓冲区当中,再将缓冲区中的数据打印到文件或者标准输出上。
    管道在读的时候,发现没有可读的数据,有两种处理方法:

    1. O_NONBLOCK disable:当前进程阻塞,就是一直在等,等到有数据可以读取。
    2. O_NONBLOCK enable:直接返回-1,errno值为EAGAIN。

当管道写满之后,也是有两种处理方法:

  1. O_NONBLOCK disable:阻塞式等待,等到有进程将管道中的数据读走。
  2. O_NONBLOCK enable:直接返回-1,errno值为EAGAIN。

  • 若是所有管道写端对应的文件描述符被关闭,则read返回0。
  • 如果所有的管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性
  • 当写入的数据量大于PIPE_BUF时,linux不保证写入的原子性

具体的匿名管道的实现代码如下:

//匿名管道的创建与使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)

int main()
{
    //1.创建管道
    int fd[2];
    char buf1[1024] = {0};
    if(pipe(fd) == -1)
    {
        perror("pipe");
        exit(1);
    }
    //2.创建子进程
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        ERR_EXIT("fork error");
    }
    //3.父进程写数据到管道
    if(pid > 0)
    {
        close(fd[0]);      //关闭管道的读端
        fgets(buf1,100,stdin);
        write(fd[1],buf1,strlen(buf1));
        close(fd[1]);
        exit(EXIT_SUCCESS);
    }
    //4. 子进程从管道中读数据并输出到屏幕上
    if(pid == 0)
    {
        close(fd[1]);
        char buf[10] = {0};
        read(fd[0],buf,6);
        printf("%s\n",buf);
    }
    return 0;
}

   这段代码主要实现的功能就是:父进程向管道中通过标准输入输入数据,子进程再通过这个管道读取到输入的数据并打印到屏幕上。
   匿名管道的特点:

  1. 只能用来具有亲缘关系的进程之间的通信,通常,管道由一个进程创建,然后再进行fork,此后,父子进程直接就可以应用该管道。
  2. 通道提供流式服务。
  3. 进程退出,管道就会被释放,管道的生命周期随进程的生命周期
  4. 内核会对管道操作惊醒同步与互斥(写满了就不写了,读完了就不读了)。
  5. 管道是半双工的,数据只能向一个方向流动,不可以两个方向同时工作。若需要双方通信,则就需要建立两个管道。
    这里写图片描述

三. 命名管道(FIFO)

命名管道是一种特殊类型的文件,也是文件的中的一种类型。
创建命名管道有两种方式:

  1. 命令行上创建

    mkfifo filename
  2. 可以在程序中创建
int mkfifo(const char* filename,mode_t mode);

命名管道与匿名管道的区别:

  1. 匿名管道由pipe函数创建并打开
  2. 命名管道由mkfifo函数创建,打开用open
  3. FIFO与pipe之间唯一的区别就在它们的创建与打开方式不同,在创建打开之后,两个管道就是相同的了。

命名管道也有相应的打开规则:
读管道时:

  • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
  • O_NONVLOCK enable:立刻返回成功

写管道时:

  • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
  • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

使用mkfifo函数来创建一个命名管道:
创建FIFO,将文件当中的数据写入到FIFO中

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//定义一个宏函数  
#define ERR_EXIT(m)\
    do\
    {\
    perror(m);\
    exit(EXIT_FAILURE);\
    }while(0)

int main()
{
    mkfifo("tp",0644);
    int infd;
    infd = open("abc",O_RDONLY);
    if(infd < 0)
        ERR_EXIT("open");

    int outfd;
    outfd = open("tp",O_WRONLY);
    if(outfd < 0)
        ERR_EXIT("open");

    char buf[1024]; 
    int n;
    while((n = read(infd,buf,1024)) > 0)
    {
        write(outfd,buf,n);
    }
    close(infd);
    close(outfd);
    return 0;
}

从FIFO中读取数据,写入到另一个文件当中

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define ERR_EXIT(m)\
    do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)

int main()
{
    int infd,outfd;
    outfd = open("back",O_CREAT | O_WRONLY | O_TRUNC,0644);
    if(outfd < 0)
        ERR_EXIT("open");

    infd = open("tp",O_RDONLY);
    if(infd < 0)
        ERR_EXIT("open");

    char buf[1024];
    int n;
    while((n = read(infd,buf,1024)) > 0)
    {
        write(outfd,buf,n);
    }
    close(infd);
    close(outfd);
    unlink("tp");
    return 0;
}

上面两段程序通过FIFO管道实现了将一个文件中的内容拷贝到另一个文件当中。

猜你喜欢

转载自blog.csdn.net/weixin_40331034/article/details/80811974