当前位置:   article > 正文

【Linux】进程间通信——管道_管道通信之popen

管道通信之popen

目录

一、概念

二、管道函数

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<stdio.h>

函数原型:

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<stdio.h>

函数原型:

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

3.文件函数

#include<unistd.h>

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<unistd.h>
  2. #include<fcntl.h>
  3. #include<sys/stat.h>
  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<unistd.h>
      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<stdio.h>
    2. #include<unistd.h>
    3. #include<stdlib.h>
    4. #include<string.h>
    5. #include<assert.h>
    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<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<assert.h>
  5. #include<fcntl.h>
  6. #include<string.h>
  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<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<assert.h>
  5. #include<fcntl.h>
  6. #include<string.h>
  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进程间通信。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/675107
推荐阅读
相关标签
  

闽ICP备14008679号