• 【Linux】进程间通信——管道


    目录

    一、概念

    二、管道函数

    1.popen函数

    2.pclose函数

    3.文件函数

    三、管道的操作

    1.管道的分类

    无名管道

    有名管道

    管道的特点

    四、管道的实现


    前言:

    进程间通信的方法/IPC机制都有哪些:

    1. 管道
    2. 套接字
    3. 信号量
    4. 共享内存
    5. 消息队列

    一、概念

    什么是管道?

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

            shell命令的连接是通过管道字符来完成,如下所示:

    cmd1|cmd2

            shell负责安排两个命令的标准输入和标准输出。cmd1的标准输入来自终端键盘,cmd1的标准输出传递给cmd2,作为它的标准输入。cmd2的标准输出来接到终端屏幕。管道是在内存上分配空间,cmd1把数据写到内存中,cmd2从内存中读取数据,效率高。

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

    图示为用管道将多个进程连接起来     

    二、管道函数

    1.popen函数

    头文件:

    #include

    函数原型:

    FILE *popen(const char * command,const char * open_mode);
    • 作用:
      • 允许一个程序将另一个程序作为新程序来启动,并可以传递数据给它或者通过它接收数据。        
    • 参数:
      • 第一个参数command字符串是要运行的程序名和相应参数。open_mode必须是“r”或者是“w”。
      • 第二个参数open_mode必须是“r”或者是“w”。如果open_mode是“r”,被调用的程序输出就可以被调用程序使用,调用程序利用popen函数返回的FILE*文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出。如果open_mode是“w”,调用程序就可以用fwrite调用向被调用程序发送数据,而被调用程序可以在自己的标准输入流上读取数据,然后做出相应的操作。
    • 返回值:
      • 失败返回空指针

    2.pclose函数

    头文件:#include

    函数原型:

    FILE *pclose(FILE *stream_to_close);
    • 作用:
      • 关闭与之关联的文件流。        
    • 返回值:
      • 通常是它所关闭的文件流所在进程的退出码。

    3.文件函数

    #include

    write:

    把缓冲区Buf的前nbytes个字节写入与文件描述符fildes关联的文件中。返回实际写入的字节数。

    size_t write(int fildes,const void *buf,size_t nbytes)

    read:

    从文件描述符filedes相关联的文件里读入nbytes个字节的数据,并且把他们放到数据区buf中。返回实际读入的字节数。

    size_t read(int dildes,void* buf,size_t nbytes);

    open:

    创建一个新的文件描述符

    1. #include
    2. #include
    3. #include
    4. int open(const char * path,int oflags);
    5. int open(const char *path,int oflags,mode_t mode);

    三、管道的操作

    管道也是个文件,用文件操作的命令对管道进程操作。管道存放在内存中,读取速度很快。

    1.管道的分类

    1. 有名管道:在任意两个进程间通信
    2. 无名管道:在父子进程间通信

    无名管道

    • 只能进行父子间通信
    • 使用pipe创建无名管道
    • pipe:
      • 头文件:
        1. #include
        2. int pipe(int file_descriptor[2]);
      • 成功返回0,失败返回-1
      • file_descriptor[0]:管道读端的描述符
      • file_descriptor[1]:管道写端的描述符
      • 两个返回的文件描述符以一种特殊的方式连接起来。写到file_deacriptor[1]的所有数据都有可以从file_descriptor[0]读回来。数据基于先进先出的原则(通常写为FIFO)进行处理,这意味着如果把字节1,2,3写到file_deacriptor[1],从file_deacriptor[0]读取到的数据也会是1,2,3,这也栈的处理方式不同,栈采用后进先出,写作LIFO。

    • 函数在两个程序之间传递数据不需要启动一个shell来解释所请求的命令,他同时还提供了对读写数据的更多控制。
    • pipe函数的参数是一个由两个整数类型的文件描述符组成数组的指针。该函数在数组中天上两个新的文件描述符后返回0,如果失败则返回-1并设置error来表示失败的原因。
    • 代码演示:父进程写入数据,子进程读取:
      1. #include
      2. #include
      3. #include
      4. #include
      5. #include
      6. int main()
      7. {
      8. int fd[2];//写端描述符
      9. assert(pipe(fd)!=-1);
      10. pid_t pid=fork();//open->fork,父子共享文件描述法
      11. assert(pid!=-1);
      12. if(pid==0)//子进程
      13. {
      14. close(fd[1]);
      15. char buff[128]={0};
      16. read(fd[0],buff,127);
      17. printf(fd[0]);
      18. }
      19. else
      20. {
      21. close(fd[0]);
      22. write(fd[1],"hello",5);
      23. close(fd[1]);
      24. }
      25. exit(0);
      26. }
    • 代码牵扯到一个知识点:先open打开文件再fork出子进程,此时,父子进程共享文件偏移量/文件描述符。注意:这里使用的文件描述符而不是文件流,所以我们必须用底层的read和write调用来访问数据,而不是用文件流库函数fread和fwrite。
    • fork,可以把fds[0][1]带给子进程,传递描述符

      子进程可以共享,管道就通过fork把管道描述符带给fork

    有名管道

    • 有名管道实现任意进程间通信
    • 有名管道的创建:
      • 创建命令:mkfifo
      • 打开管道:open();
      • 读数据:read();
      • 写入数据:write();
      • 关闭管道:close()
    • 代码实现有名管道间通信:

    a.c:读取fifi的管道文件,写入5个字符的"hello"数据进去。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main()
    8. {
    9. int fd=open("fifi",O_WRONLY);
    10. assert(fd!=-1);
    11. printf("fd=%d\n",fd);
    12. write(fd,"hello",5);
    13. close(fd);
    14. }

    b.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main()
    8. {
    9. int fd=open("./fifi",O_RDONLY);
    10. assert(fd!=-1);
    11. printf("fd=%d\n",fd);
    12. char buff[128]={0};
    13. read(fd,buff,127);//不读\0
    14. printf("read %s\n",buff);
    15. close(fd);
    16. }

    管道的特点

    1. 管道必须读,写进程同时open会阻塞
    2. 如果管道中没有数据,那么read就会堵塞
    3. 管道的写端关闭,读read返回值为0,终止程序
    4. 管道的读端关闭,写端写入时产生信号,终止程序
    5. 管道打开的时候只有只读只写两种方式,不能是读写方式,因为读写方式是未定义的
    6. 管道是半双工的:
      1. 全双工、单工、半双工、
      2. 全双工:任意时刻都是双向的
      3. 半双工:某一时刻只能是一个方向
      4. 单:发送方和接收方只有一个方向
    7. 管道文件大小永远为0

    四、管道的实现

    open打开管道后,会开辟如下所示大小的空间:

    头指针:当前待写入的数据位置

    尾指针:当前待读取数据的位置,图示为即将读取h

    abcdefg为已经读取的数据,用户使用过的空间可以被重新利用,当前空间满了可以写入在缓冲区指针位置。--相当于循环队列

    IPC机制有:IPC进程间通信。

  • 相关阅读:
    PowerBI 8月更新,数据标签条件格式
    项目实战 Java读取Excel数据
    深度学习笔记其六:现代卷积神经网络和PYTORCH
    ES集群安装遇到的一些错误
    【最新】TensorFlow、cuDNN、CUDA三者之间的最新版本对应及下载地址
    JavaWeb笔记
    ModbusTCP 转 Profinet 主站网关控制汇川伺服驱动器配置案例
    Visual Studio Code的安装和使用
    小程序video标签在底部出现1px无法去除的黑色线
    LeetCode·每日一题·1374.生成每种字符都是奇数个的字符串·模拟
  • 原文地址:https://blog.csdn.net/qq_53830608/article/details/127518810