我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

img

进程间通信

1.1进程间通信介绍

什么是进程间通信?

答:进程具有独立性,每个进程都有自己的PCB,所以进程间需要通信,并且通信的成本一定不低(通信的本质:OS需要直接或者间接给通信双方的进程提供“内存空间”,并且要通信的进程,必须看到一份公共的资源)

而我们所说的不同通信种类本质就是:上面所说的资源,是OS中的哪一个模块提供的。如文件系统提供的叫管道通信;OS对应的System V模块提供的…

📝ps:成本不低是因为我们需要让不同的进程看到同一份资源

1.2进程间通信目的

进程间通信的目的在于

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程)

为什么要有进程间通信?

答:有时候我们需要多进程协同的,完成某种业务内容。比如管道

1.3进程间通信分类

如何去通信

答:1.采用标准的做法:System V进程间通信(聚焦在本地通信,如共享内存)、POSIX进程间通信(让通信过程可以跨主机)。

采用文件的做法:管道-基于文件系统(匿名管道、命名管道)

而本篇博客主要介绍管道,接着往下看把👇


管道

2.1管道介绍

管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个"管道"

任何一个文件包括两套资源:1.file的操作方法 2.有属于自己的内核缓冲区,所以父进程和子进程有一份公共的资源:文件系统提供的内核缓冲区,父进程可以向对应的文件的文件缓冲区写入,子进程可以通过文件缓冲区读取,此时就完成了进程间通信,这种方式提供的文件称为管道文件。管道文件本质就是内存级文件,不需要IO

两个进程如何看到同一个管道文件:fork创建子进程完成

管道创建时分别以读和写方式打开同一个文件(如果只读或者只写,子进程也只会继承只读或只写,父子双方打开文件的方式一样,无法完成单向通信);父进程创建子进程,父进程以读写打开,子进程也是以读写打开(一般而言,管道只用来进行单向数据通信);关闭父子进程不需要的文件描述符,完成通信

image-20230124234013245

管道分为匿名管道和命名管道

2.2匿名管道

我们通过文件名区分文件,但是如果当前进程的文件没有名字,这样的内存级文件称为匿名管道。让两个进程看到同一个文件,通过父进程创建子进程,子进程继承文件地址的方式,看到同一个内存级文件,此时内存级文件没有名称就是匿名管道了。匿名管道能用来父进程和子进程之间进行进程间通信。

pipe

pipe:创建一个管道只需要调用pipe,注意头文件,返回值,以及函数的参数

头文件为#include ,调用成功返回0,调用失败返回-1。参数是输出型参数

SYNOPSIS       #include        int pipe(int pipefd[2]);DESCRIPTION    pipe() creates a pipe,pipefd[0]  refers  to  the  read end of the pipe.  pipefd[1] refers to the write end of the pipe.RETURN VALUE       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

创建管道文件,打开读写端

#include #include #include using namespace std;int main(){    int fds[2];    int n = pipe(fds);    assert(n == 0);    //0,1,2->3,4    //[0]:读取  [1]:写入    cout<<"fds[0]:"<

image-20230125000717496

所以[0]:3代表读取👄,[1]:4代表写入✍。

fork子进程:

#include #include #include #include #include using namespace std;int main(){    int fds[2];    int n = pipe(fds);    assert(n == 0);    //fork    pid_t id = fork();    assert(id>=0);    if(id==0)    {        //子进程通信        exit(0);    }    //父进程通信    n = waitpid(id,nullptr,0);    assert(n==id);    return 0;}

关闭父子进程不需要的文件描述符,完成通信

子进程写入,父进程读取:

#include #include #include #include #include #include #include #include #include using namespace std;int main(){    int fds[2];    int n = pipe(fds);    assert(n == 0);    //fork    pid_t id = fork();    assert(id>=0);    if(id==0)    {        //子进程通信:子进程进行写入,关闭读        close(fds[0]);        //通信        const char*s = "这是子进程,正在进行通信";        int cnt = 0;        while(true)        {            cnt++;            char buffer[1024];            snprintf(buffer,sizeof buffer,"child->parent say:%s[%d][%d]",s,cnt,getpid());            //写端写满的时候,在写会阻塞,等对方进行读取            write(fds[1],buffer,strlen(buffer));//系统接口            sleep(1);//一秒写一次        }        //退出前关闭子进程        close(fds[1]);        exit(0);    }    //父进程通信:父进程进行读取,关闭写    close(fds[1]);    //通信    while(true)    {        char buffer[1024];        //管道中如果没有数据,读端在读,默认会直接阻塞当前正在读取的进程        ssize_t s = read(fds[0],buffer,sizeof(buffer)-1);        if(s>0) buffer[s] = 0;        cout<<"Get Message# "<

image-20230125113529871

读写特征

管道读写特征

1.读快写慢

子进程休眠时,不在写入,父进程在读取(如果管道中没有数据,读端在读,此时默认会直接阻塞当前正在读取的进程)

image-20230125141424118

2.读慢写快

拿着管道读端不读,写端一直在写:写端往管道里写,而管道是有大小的,不断往写端写,会被写满

image-20230125145331180

管道是固定大小的缓冲区,当管道被写满,就不能再写了。此时写端会阻塞。

如果父进程只是sleep(2),稍微睡眠比较少:

image-20230125145632803

在这里不断读取的时候:写端是把数据塞到管道里,管道读取的是按照指定大小读取!而不是一行一行。而我们刚开始按行读取的是因为发送的慢,一次塞一行数据。

3.写入关闭,读到0

子进程写入端关闭:

image-20230125150745054

4.读取关闭,写入

管道是单向的:读端关闭,在写入就没有意义了:OS会终止写端,会给写进程发送信号,终止写端

image-20230125154034441

管道特征

1.管道的生命周期随进程,进程退出,管道释放

2.管道可以用来进行具有血缘关系的进程间通信(常用于父子通信)

3.管道是面向字节流的

4.半双工—单向通信(特殊)

5.互斥与同步机制——对共享资源进行保护的方案


2.3命名管道

我们前面已经知道:匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

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

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

mkfifo

NAME       mkfifo - make FIFOs (named pipes)SYNOPSIS    #include     #include     int mkfifo(const char *pathname, mode_t mode);RETURN VALUE       On success mkfifo() returns 0.  In the case of an error, -1 is returned (in which case, errno is set appropriately).

在当前路径下直接创建命名管道:

mkfifo named_pipe

往管道文件写东西:

image-20230128133649932

image-20230128133743211

image-20230128133826611

两个进程打开同一个文件:站在内核的角度,第二个文件不需要继续创建struct file对象,因为OS会识别到打开的文件被打开了。在内核中,此时就看到了同一份资源,有着操作方法和缓冲区,不需要把数据刷新到磁盘上去,不需要IO。所以无论是匿名还是命名,本质都是管道

匿名管道通过继承的方式看到同一份资源。命名管道:通过让不同的进程打开指定名称(路径+文件名,具备唯一性)的同一个文件看到同一份资源。所以命名管道是通过文件的文件名来标定唯一性的。而匿名管道是通过继承的方式来标定的

创建管道文件

准备工作

分为三个文件:comm.hpp:公共文件(同一份资源),server.cc:读取端,clinet.cc:写入端

在目录tmp下创建文件:

server.cc:

#include "comm.hpp"int main(){    bool ret = createFifo(NAMED_PIPE);    assert(ret);    (void)ret;    return 0;}

comm.hpp:

#pragma once#include #include #include #include #include #include #include #define NAMED_PIPE "/tmp/mypipe.name"bool createFifo(const std::string &path){    umask(0);    int n = mkfifo(path.c_str(),0666);    if(n==0) return true;    else    {        std::cout<<"errno:"<

运行:

删除管道文件

unlink

注意头文件,函数的参数以及返回值这三个主要部分:

NAME       unlink - remove a directory entrySYNOPSIS       #include        int unlink(const char *path);RETURN VALUE       Upon successful completion, 0 shall be returned. Otherwise, -1 shall be returned and errno set to indicate the error. If -1 is returned, the named file shall not be changed.

在comm.hpp中封装好删除的函数:

void removeFifo(const std::string &path){    int n = unlink(path.c_str());    assert(n==0);    (void)n;//防止n没使用而警告}

在server.cc中进行调用:

#include "comm.hpp"int main(){    bool ret = createFifo(NAMED_PIPE);    assert(ret);    (void)ret;    removeFifo(NAMED_PIPE);    return 0;}

至此,创建和删除管道文件的操作我们实现完毕。下面进入通信环节

通信

其实在了解完了匿名管道之后,对于命名管道我们能够更好的理解:

client.cc(写端):

#include "comm.hpp"int main(){    int wfd = open(NAMED_PIPE,O_WRONLY);    if(wfd<0) exit(1);    //write    char buffer[1024];    while(true)    {        std::cout<<"Please Say:";        fgets(buffer,sizeof(buffer),stdin);        //if(strlen(buffer)>0) buffer[strlen(buffer)-1] = 0;        ssize_t n = write(wfd,buffer,strlen(buffer));        assert(n==strlen(buffer));        (void)n;    }    close(wfd);    return 0;}

server.cc(读端):

#include "comm.hpp"int main(){    bool ret = createFifo(NAMED_PIPE);    assert(ret);    (void)ret;    int  rfd = open(NAMED_PIPE,O_RDONLY);    if(rfd<0) exit(1);    //read    char buffer[1024];    while(true)    {        ssize_t s = read(rfd,buffer,sizeof(buffer)-1);        if(s>0)        {            buffer[s] = 0;            std::cout<<"client->server" <

进行通信:

image-20230128145203280

读端多出一行空行:写端输入之后多按了回车,修改为buffer[strlen(buffer)-1] = 0;

image-20230128154255185


总结

进程间通信的内容是比较多的,在这里,本文只是对进程间通信——管道这一部分进行介绍,后续会继续更新其他部分。

我们从进程间通信开始介绍,而后进入了进程间通信——管道这部分,管道又分为匿名管道和命名管道,以及之间的区别,匿名管道需要具有血缘关系的进程,而命名管道则不需要,同时,匿名管道通过子进程继承文件地址的方式,看到同一个内存级文件,而命名管道通过不同进程打开同一个文件,看到同一份资源。至此,对于管道的理解我们就先到这里结束。

来源地址:https://blog.csdn.net/weixin_60478154/article/details/128779029

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

文章目录 进程间通信1.1进程间通信介绍1.2进程间通信目的1.3进程间通信分类 管道2.1管道介绍2.2匿名管道pipe读写特征管道特征 2.3命名管道mkfifo创建管道文件删除管道文件通信 总结
2023-08-24

Python进程间通信之命名管道(Win

前面文章说了一下 Linux 命名管道的实现,今天看看 Windows 上我们怎么实现。在 Windows 上的命名管道主要是通过调用 win32 api 的以下方法来实现的: - win32pipe.CreateNamedPipe() -
2023-01-31

Linux-进程间通信

进程间通信 进程间通信介绍进程间通信目的进程间通信发展进程间通信分类 管道匿名管道匿名管道特点匿名管道读写规则 命名管道创建一个命名管道命名管道的打开规则用命名管道实现server&client通信 system
2023-08-19

【Linux】进程间通信(万字详解)—— 匿名管道 | 命名管道 | System V | 共享内存

🌈欢迎来到Linux专栏~~进程通信 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C++中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一
2023-08-22

Linux进程间通信的方式

这篇文章主要介绍“Linux进程间通信的方式”,在日常操作中,相信很多人在Linux进程间通信的方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux进程间通信的方式”的疑惑有所帮助!接下来,请跟着小编
2023-06-16

Linux进程间通信怎么实现

这篇文章主要讲解了“Linux进程间通信怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux进程间通信怎么实现”吧!共享内存共享内存可以说是最有用的进程间通信方式,也是最快的IP
2023-07-05

Linux中怎么实现管道通信

Linux中怎么实现管道通信,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、定义管道是单向的、先进先出的。它将一个程序的输入和另一个程序的输出连接起来。数据被
2023-06-13

python 进程间通信

python multiprocessingmultiprocessing在2.6才开始使用multiprocessing 是一个使用方法类似threading模块的进程模块。允许程序员做并行开发。并且可以在UNIX和Windows下运行。
2023-01-31

进程间的通信

使用 multiprocessing 里的 Queue()import multiprocessingdef download_from_web(q): """下载数据""" # 模拟从网上下载的数据 data = [11
2023-01-30

Linux进程间通信的方式有哪些

本文小编为大家详细介绍“Linux进程间通信的方式有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“Linux进程间通信的方式有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.管道管道分为有名管道和无名
2023-06-28

Linux进程间通信的方式是什么

本篇内容主要讲解“Linux进程间通信的方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux进程间通信的方式是什么”吧!·进程间通信:操作系统为系统提供的用于实现进程间通信的方式进
2023-06-29

Linux进程间的通信方式有哪些

本篇内容主要讲解“Linux进程间的通信方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux进程间的通信方式有哪些”吧!进程的概念进程是操作系统的概念,每当我们执行一个程序时,对于
2023-06-16

Linux进程间通信——使用流套接字

前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程。通常我们使用socket进行网络编程,这里将会简单地讲述如何使用socke
2022-06-04

Linux操作系统 进程之间的通信

进程之间的通信预备知识:1、用户态和内核态,当一个进程在执行用户自己的代码时处于用户运行态(用户态);当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态)。2、进程之间的通信(Inter Processs Communica
2023-06-05

golang管道如何用于函数间通信

管道在 go 语言中是一种无缓冲通道,用于在 goroutine 之间传输数据。它允许 goroutine 异步通信,提高程序效率和可扩展性。管道可以用于写入和读取数据,使用 Go 语言中的管道:函数间通信机制管道是一种用于在 Gorou
golang管道如何用于函数间通信
2024-05-02

编程热搜

目录