当前位置:   article > 正文

Linux管道

linux管道

目录

1.管道概念

2.管道分类

        1.匿名管道

        1.基本实现与概念

        2.站在文件描述符角度-深度理解管道

        3.站在内核角度-管道本质

        4.管道读写规则

        5.管道属性设置与阻塞验证       

         6.管道特点(匿名)

        2.命名管道

        1.创建一个命名管道

        2.命名管道的打开规则 

 3.匿名管道与命名管道的区别


1.管道概念

        管道是Linux中的最古老的通信方式;

        我们把一个进程链接到另一个进程的一个数据流称为一个"管道";

2.管道分类

        1.匿名管道

        1.基本实现与概念

        头文件: #include <unistd.h>

        功能:创建一无名管道原型int pipe(int fd[2]);

        参数fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端返回值:成功返回0,失败返回错误代码

         通常用fork函数来共享管道;接下来我们来实现一下匿名管道的基本读写的实现:

                                                                不关闭描述符

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <wait.h>
  5. int main(){
  6. int fd[2];
  7. int ret=pipe(fd);
  8. if(ret<0){
  9. perror("pipe");
  10. return 0;
  11. }
  12. pid_t pid=fork();
  13. if(pid<0){
  14. perror("fork");
  15. return 0;
  16. }else if(pid==0){
  17. //child
  18. //子进程进行接受
  19. char buf_read[1024]={0};
  20. read(fd[0],buf_read,sizeof(buf_read)-1);
  21. printf("The content is :%s\n",buf_read);
  22. }else{
  23. //father
  24. //父进程发送数据
  25. char buf_write[]="success!";
  26. write(fd[1],buf_write,strlen(buf_write));
  27. wait(NULL);
  28. }
  29. return 0;
  30. }

                                                                运行结果

        我们可以看出来在父进程向管道写入信息,在子进程读取管道信息. 

                                                          关闭描述符

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <wait.h>
  5. int main(){
  6. int fd[2];
  7. int ret=pipe(fd);
  8. if(ret<0){
  9. perror("pipe");
  10. return 0;
  11. }
  12. pid_t pid=fork();
  13. if(pid<0){
  14. perror("fork");
  15. return 0;
  16. }else if(pid==0){
  17. //child
  18. //子进程进行接受
  19. close(fd[1]);
  20. char buf_read[1024]={0};
  21. read(fd[0],buf_read,sizeof(buf_read)-1);
  22. printf("The content is :%s\n",buf_read);
  23. }else{
  24. //father
  25. //父进程发送数据
  26. close(fd[0]);
  27. char buf_write[]="success!";
  28. write(fd[1],buf_write,strlen(buf_write));
  29. wait(NULL);
  30. }
  31. return 0;
  32. }

                                                        运行结果 

                我们可以看出依旧可以进行读写~原因是什么呢?

        2.站在文件描述符角度-深度理解管道

 

        首先父进程创建管道,系统默认打开三个文件:标准输入,标准输出,和标准错误;创建管道则有fd[0],fd[1];分别表示管道的读端和写端;

         然后父进程创建子进程,子进程拷贝父进程,也拷贝了管道信息;所以父子进程共享管道;

         父进程关闭读端,子进程关闭写端,但是管道是共享的,依旧可以进行信息交互;

        3.站在内核角度-管道本质

 看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。

        4.管道读写规则

        当没有数据可读时O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

        当管道满的时候O_NONBLOCK disable: write调用阻塞,直到有进程读走数据O_NONBLOCK enable:调用返回-1,errno值为EAGAIN如果所有管道写端对应的文件描述符被关闭,则read返回0   

        如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

        当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

        当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

        5.管道属性设置与阻塞验证       

        fcntl函数原型:

  1. #include<unistd.h>
  2. #include<fcntl.h>
  3. int fcntl(int fd, int cmd);
  4. int fcntl(int fd, int cmd, long arg);
  5. int fcntl(int fd, int cmd ,struct flock* lock);

 (1)F_DUPFD

与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。

(2)F_GETFD

读取文件描述符close-on-exec标志

(3)F_SETFD

将文件描述符close-on-exec标志设置为第三个参数arg的最后一位

(4)F_GETFL

获取文件打开方式的标志,标志值含义与open调用一致

(5)F_SETF

设置文件打开方式为arg指定方式

设置读端为非阻塞:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. int main(){
  5. int fd[2];
  6. int ret=pipe(fd);
  7. if(ret<0){
  8. perror("pipe");
  9. return 0;
  10. }
  11. pid_t pid=fork();
  12. if(pid<0){
  13. perror("fork");
  14. return 0;
  15. }else if(pid==0){
  16. //child
  17. //关闭写端
  18. //设置读端为非阻塞属性
  19. //
  20. close(fd[1]);
  21. int flag=fcntl(fd[0],F_GETFL);
  22. fcntl(fd[0],F_SETFL,flag|O_NONBLOCK);
  23. char buf_read[1024]={0};
  24. ssize_t read_size=read(fd[0],buf_read,sizeof(buf_read)-1);
  25. printf("read_size:%ld\n",read_size);
  26. perror("read");
  27. }else{
  28. //father
  29. //关闭读端
  30. //写关闭/不关闭
  31. close(fd[0]);
  32. close(fd[1]);
  33. }
  34. while(1){
  35. sleep(1);
  36. }
  37. return 0;
  38. }

                                                                        运行结果为:

 设置写端非阻塞:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. int main(){
  5. int fd[2];
  6. int ret=pipe(fd);
  7. if(ret<0){
  8. perror("pipe");
  9. return 0;
  10. }
  11. pid_t pid=fork();
  12. if(pid<0){
  13. perror("fork");
  14. return 0;
  15. }else if(pid==0){
  16. //child
  17. //关闭子进程读端
  18. //设置子进程写段为非阻塞
  19. //子进程写
  20. close(fd[0]);
  21. int flag=fcntl(fd[1],F_GETFL);
  22. fcntl(fd[1],F_SETFL,flag|O_NONBLOCK);
  23. ssize_t write_size;
  24. while(1){
  25. write_size=write(fd[1],"a",1);
  26. if(write_size<0){
  27. break;
  28. }
  29. }
  30. printf("write_size:%ld\n",write_size);
  31. }else{
  32. //father
  33. //关闭父进程写端
  34. //关闭父进程读端/不关闭
  35. close(fd[1]);
  36. //close(fd[0]);
  37. while(1){
  38. sleep(1);
  39. }
  40. }
  41. return 0;
  42. }

                                                运行结果:

         6.管道特点(匿名)

        只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;

        通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。

        管道提供流式服务一般而言,进程退出,管道释放,所以管道的生命周期随进程一般而言,内核会对管道操作进行同步与互斥管道是半双工的;

        数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

        2.命名管道

        管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

        如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

        命名管道是一种特殊类型的文件

        1.创建一个命名管道

$ mkfifo pipe1

执行结果: 

也可以程序创建

函数原型:

  1. #include<sys/types.h>
  2. #include<sys/stat.h>
  3. int mkfifo(const char *filename,mode_t mode);

                                        代码如下: 

  1. #include <stdio.h>
  2. #include<sys/types.h>
  3. #include<sys/stat.h>
  4. int main(){
  5. mkfifo("pipe2",0664);
  6. return 0;
  7. }

                                        运行结果

        2.命名管道的打开规则 

        如果当前打开操作是为读而打开FIFO时

        O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO

        O_NONBLOCK enable:立刻返回成功如果当前打开操作是为写而打开FIFO时

        O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO 

        O_NONBLOCK enable:立刻返回失败,错误码为ENXIO                

 3.匿名管道与命名管道的区别

        匿名管道由pipe函数创建并打开。

        命名管道由mkfifo函数创建,打开用openFIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

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

闽ICP备14008679号