Liunx操作-Record18—进程间通讯的概念及PIPE管道通信相关的操作

目录

IPC通信

概念

IPC通信的方式

进程间通信的目的

常见的通信方式

pipe 管道(半双工通信方式)

运行原理

如何使用

管道的读写行为

读管道

写管道

管道的大小和优劣

管道的大小

管道的优劣

优点

缺点


IPC通信

概念

进程间通讯,创建的两个进程之间是彼此独立的,那两个进程之间如果想打交道的时候,通过内核,提供了一块儿缓冲区(buffer),实现了数据的一个交换,这种机制就叫做InterProcess Communication。

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

IPC通信的方式

pipe 管道——最简单 (只支持“有血缘关系”的进程)

fifo 有名管道——是对pipe管道类型的改进加强版(囊括了有血缘关系和没有血缘关系的所有进程间,都可以通信)

mmap 文件映射共享IO——速度最快(在内存中开一块缓冲区,然后,将文件映射到内存上,然后,直接操作内存就可以实现控制了)

本地socket——最稳定

信号——携带信息量最小的

共享内存——在内存中用特定API申请的一块儿内存区,这块区域相当于是共享的,当开辟这块内存的进程退了,这块内存区域还是可以保留下来,其他进程可以去访问这块儿区域。

消息队列

进程间通信的目的

通过进程之间的互相通信,共同完成一件事情。

常见的通信方式

有三种:单工(广播)、半双工(同一个时刻数据只能往一个方向流,比如:对讲机)、全双工(可以同时刻双向数据流通,比如:打电话)

pipe 管道(半双工通信方式)

运行原理

当需要通信的时候,通过内核创建一个伪文件(相当于就是刚才说的缓冲区,这个缓冲区留了两个端口,一端是读,一端是写,类似于一个管道),

过程就是,先pipe一下,创建管道,然后,一个进程负责往里面写,另一个进程负责往外面读,这样的话,就达到了数据交换的目的,在这个过程中,需要注意的是,对于管道的创建时间,是在子进程的创建之前啊,还是子进程的创建之后呢?很明显,应该是在子进程的创建之前进行,只有这样,子进程才能共享两个端口,整个过程应该是父进程先创建pipe管道,然后,建立读写端口之间的联系(或者说建立与伪文件之间的联系),然后,子进程在和这个两个端口建立联系,这样,就实现了父子进程之间的共享了!

 

本节所说的管道就是半双工通信的方式。

如何使用

管道函数:int pipe(int pipefd[2]);

  • pipefd 读写文件描述符 ,0-代表读,1-代表写
  • 返回值:失败返回-1,成功返回0

父子进程实现pipe通信,实现ps aux|grep bash 功能

#include <stdio.h>
#include <unistd.h>

int main()
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();
    if(pid == 0){
        //son 
        //son -- > ps 
        //关闭 读端
        close(fd[0]);
        //1. 先重定向
        dup2(fd[1],STDOUT_FILENO);//标准输出重定向到管道写端
        //2. execlp 
        execlp("ps","ps","aux",NULL);
    }else if(pid > 0){
        //parent
        //关闭写端
        close(fd[1]);
        //1. 先重定向,标准输入重定向到管道读端
        dup2(fd[0],STDIN_FILENO);
        //2. execlp 
        execlp("grep","grep","bash",NULL);
    }

    return 0;
}

其中,grep命令就是过滤内容的,如果grep后面接入内容,其效果是等待标准输入。 

运行如下:

以这段代码为例,

 

管道的读写行为

读管道

  • 写端全部关闭——read读到0,相当于读到文件末尾
  • 写端没有全部关闭
  • 有数据—read读到数据
  • 没有数据—read阻塞 (fcntl函数可以更改非阻塞)

写管道

  • 读端全部关闭——会产生一个信号SIGPIPE(程序异常中止,用这个信号报错)
  • 读端未全部关闭
  • 管道已满—write阻塞
  • 管道未满—write正常写入

实现效果演示效果的代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    int fd[2];
    pipe(fd);
    pid_t pid = fork();

    if(pid == 0){
        //son 
        sleep(3);
        close(fd[0]);//关闭读端
        write(fd[1],"hello",5);
        close(fd[1]);
        while(1){
            sleep(1);
        }
    }else if(pid > 0){
        //parent 
        close(fd[1]);//关闭写端
        close(fd[0]);
        int status;
        wait(&status);
        if(WIFSIGNALED(status)){
            printf("killed by %d\n",WTERMSIG(status));
        }
        //父进程只是关闭读写两端,但是不退出
        while(1){
            sleep(1);
        }

        char buf[12]={0};
        while(1){

            int ret = read(fd[0],buf,sizeof(buf));
            if(ret == 0){
                printf("read over!\n");
                break;
            }
            if(ret > 0){
                write(STDOUT_FILENO,buf,ret);
            }
        }
    }
    return 0;
}

运行效果正常。

管道的大小和优劣

管道的大小

可以使用ulimit –a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。

也可以使用fpathconf函数,借助参数->选项来查看。使用该宏应引入头文件<unistd.h>

long fpathconf(int fd, int name);

成功:返回管道的大小 ;失败:-1

管道的优劣

优点

  • 简单,相比信号,套接字实现进程间通信,简单很多。

缺点

  • 1. 只能单向通信,双向通信需建立两个管道。(如果想父进程给进程发,子进程给父进程发,得创建两根管道,没法实现双向通信)
  • 2. 只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用fifo有名管道解决。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值