进程间通信(pipe管道)

         Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷贝到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制成为进程间通信(IPC)

进程间通信的方式

最常用:

  • 管道(最简单)
  • 信号(开销最小)
  • 共享映射区(无血缘关系)
  • 本地套接字 (最稳定)        

管道

 管道的原理:

  • 管道的实质是内核缓冲区,内部使用环形队列实现
  • 默认的缓冲区大小为4K,可以使用ulimit -a 命令获取大小
  • 实际操作过程中缓冲区会根据数据压力做适当调整
  • 数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道
  • 只能在有血缘关系的进程间使用管道
  • 管道的读写两端都是阻塞的
  • 数据被读走之后,在管道中就消失了。

创建管道 -pipe函数

  • 函数作用:创建一个管道
  • 函数原型:
    • int pipe(int fd[2]);
    • int pipe(int *fd);
  • 函数参数:
    • 若调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端
  • 返回值:
    • 成功返回0
    • 失败返回-1,并设置errno值
  • 操作流程:
    • 父进程创建管道pipe
    • 父进程调用fork函数创建子进程
    • 父进程关闭一端
    • 子进程关闭一端
    • 父进程和子进程分别执行read或者write操作

px -aux | grep bash 思路解析

 操作流程:

  1. 创建管道pipe
  2. 创建子进程fork
  3. 父进程关闭读端fd[0]
  4. 子进程关闭写端fd[1]
  5. 在父进程中将标准输出重定向到管道写端 dup2(fd[1],STDOUT_FILENO);
  6. 在子进程中将标准输入重定向到管道读端 dup2(fd[0],STDIN_FILENO);       
  7. 在父进程中调用execl函数执行ps aux命令
  8. 在子进程中调用execl函数执行grep bash命令

代码示例:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<sys/types.h>
  5 #include<unistd.h>
  6 #include<sys/wait.h>
  7 
  8 
  9 int main()
 10 {
 11     //创建管道
 12     //int pipe(int pipefd[2])
 13     int fd[2];
 14     int ret = pipe(fd);
 15     if(ret<0)
 16     {
 17         perror("pipe error");
 18         return -1;
 19     }
 20 
 21     //创建子进程
 22     pid_t pid = fork();
 23     if(pid<0)
 24     {
 25         perror("fork error");
 26     }
 27     else if(pid>0)
 28     {
 29         //关闭读端
 30         close(fd[0]);
 31         //将标准输出重定向到管道的写端
 32         dup2(fd[1],STDOUT_FILENO);
 33         
 34         execlp("ps","ps","aux",NULL);
 35         wait(NULL);
 36     
 37     }else
 38     {   
 39         //关闭写端
 40         close(fd[1]);
 41         //将标准输入重定向到管道的读端
 42         dup2(fd[0],STDIN_FILENO);
 43         execlp("grep","grep","bash",NULL);
 44     }
 45     return 0;
 46 }
~                          

管道的读写行为

  • 读操作
    • 有数据:read正常读,返回读出的字节数
    • 无数据
      • 写端全部关闭:read解除阻塞,返回0,相当于读文件到了尾部
      • 没有全部关闭:read会阻塞
  • 写操作
    • 读端全部关闭:管道破裂,进程终止,内核给当前进程发SIGPIPE信号
    • 读端没有全部关闭:
      • 缓冲区写满了:write阻塞
      • 缓冲区没有满:继续write

如何设置管道为非阻塞

        默认情况下,管道的读写两端都是阻塞的,若要设置成读或者写为非阻塞要进行下面步骤

  1. int flags = fcntl(fd[0],F_GETFL,0);
  2. flag | = O_NONBLOCK;
  3. fcntl(fd[0],F_SETFL,flags)
  • 若是读端为非阻塞:
    • 写端没有关闭,管道中没有数据可读,read返回-1
    • 写端没有关闭,read中有数据可读,则read返回实际读到的字节数
    • 写端已经关闭,管道中有数据可读,同上
    • 写端已经给关闭,管道中没有数据可读,则read返回0

非阻塞代码与运行结果

  1.   1 
        //非阻塞代码与运行结果
        #include<stdio.h>
      2 #include<stdlib.h>
      3 #include<string.h>
      4 #include<sys/types.h>
      5 #include<unistd.h>
      6 #include<sys/wait.h>
      7 #include<fcntl.h>
      8 
      9 int main()
     10 {
     11     //创建管道
     12     //int pipe(int pipefd[2])
     13     int fd[2];
     14     int ret = pipe(fd);
     15     if(ret<0)
     16     {
     17         perror("pipe error");
     18         return -1;
     19     }
     20     
     21     //设置管道的读端为非阻塞
     22     int flag = fcntl(fd[0],F_GETFL);
     23     flag |= O_NONBLOCK;
     24     fcntl(fd[0],F_SETFL,flag);
     25         
     26         //关闭写端
     27         
     28     char buf[64];
     29     memset(buf,0x00,sizeof(buf));
     30     int n=read(fd[0],buf,sizeof(buf));
     31     printf("read over,n==[%d],buf==[%s]\n",n,buf);
     32     
     33     
     34     return 0;
     35 }
    ~    
    
    root@lxy-virtual-machine:~/test/course/day6# make pipe2
    cc     pipe2.c   -o pipe2
    root@lxy-virtual-machine:~/test/course/day6# ./pipe2
    read over,n==[-1],buf==[]               

    阻塞代码与运行结果

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<sys/types.h>
  5 #include<unistd.h>
  6 #include<sys/wait.h>
  7 #include<fcntl.h>
  8 
  9 int main()
 10 {
 11     //创建管道
 12     //int pipe(int pipefd[2])
 13     int fd[2];
 14     int ret = pipe(fd);
 15     if(ret<0)
 16     {
 17         perror("pipe error");
 18         return -1;
 19     }
 20 
 21     //设置管道的读端为非阻塞
 22     
 23         //关闭写端
 24         
 25     char buf[64];
 26     memset(buf,0x00,sizeof(buf));
 27     int n=read(fd[0],buf,sizeof(buf));
 28     printf("read over,n==[%d],buf==[%s]\n",n,buf);
 29     
 30     
 31     return 0;
 32 }   
~              

root@lxy-virtual-machine:~/test/course/day6# make pipe3
cc     pipe3.c   -o pipe3
root@lxy-virtual-machine:~/test/course/day6# ls
pipe  pipe2  pipe2.c  pipe3  pipe3.c  pipe.c  ps  ps.c
root@lxy-virtual-machine:~/test/course/day6# ./pipe3
s




^Z
[1]+  已停止               ./pipe3

如何查看管道缓冲区大小

  • 命令:ulimit -a
  • 函数
    • long fpathconf(int fd,int name);
    • printf("pipe size==[%ld]\n",fpathconf(fd[0],_PC_PIPE_BUF))
    • printf("pipe size==[%ld]\n",fpathconf(fd[1],_PC_PIPE_BUF))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值