(25)Linux IPC 进程间通信系统调用:pipe接口

一、进程间通信(IPC)

1、为什么要进程间通信?

我们在之前讲过 "进程之间是具有独立性" 的,如果进程间想交互数据,成本会非常高!

因为独立性之本质即 "封闭",进程们你封闭你的我封闭我的,那么进程间的交流可谓是窒碍难行。

进程间的通信说白了就是 "数据交互",我们需要多进程进行协同处理一件事情。

  • 刚才说的是宏观上的概念,下面我们来看看具体的、为什么要进行通信:
  •     数据传输:一个进程需要将它的数据发送给另一个进程
  •     资源共享:多个进程之间共享资源
  •     通知事件:一个进程需要向另一个或一组进程发送讯息,通知它 (它们) 发生了某种事件(比如进程终止时要通知父进程)
  •     进程控制:有些进程希望完全控制另一个进程的执行(如 debug 进程),此时控制进程希望能够拦截另一个进程的所有陷阱和异常,并能够及时知道它的状态改变,属于 "闭环控制"。

进程间通信的必要性:

  • 单进程的,那么也就无法使用并发能力,更加无法实现多进程协同
  • 传输数据,同步执行流,消息通知等

2、程间通信的技术背景

  • 1.进程是具有独立性的。虚拟地址空间+页表 保证进程运行的独立性(进程内核数据结构+进程的代码和数据)
  • 2.通信成本会比较高! 

3、进程间通信的本质理解

  • 1.进程间通信的前提,首先需要让不同的进程看到同一·块“内存"(特定的结构组织的)
  • 2.所以你所谓的进程看到同一块"内存",属于哪一个进程呢?不能隶属于任何一个进程,而应该更强谢共享 

进程间通信的方式 也有一些标准
1.Linux原生能提供 - 管道-->匿名 命名
2.Systemy ---多进程 -- 单机通信
共享内存
消息队列(不常用)
信号量(不讲 - 原理)
3.posix-- 多线程----网络通信 

标准更多在我们使用者看来,都是接口上具有一定的规律

二、管道(PIPE) 

1、何为管道? 

何为管道?管道是 \textrm{Unix} 系统中最古老的 IPC 形式,

将一个进程连接到另一个进程的数据流称为管道 (Pipe)。

 

 下面我们先来讲解 匿名管道 (Anonymous Pipe) !

2、匿名管道(Anonymous Pipe) 

 匿名管道是计算机进程间的一种 单工 先进先出通信机制,全双工通信 通常需要两个匿名管道。

举个例子:假设内存中有两个独立的进程 A 和 B,我们想让 AB 之间进行进程间通信。

* 令 A 先把数据拷贝到磁盘上,再让 B 去读取该数据,如下图所示:

​我们可以通过这个例子明白一个道理:通信之前,要让不同的进程看到同一份资源。 

现阶段我们要学的进程间通信,不是如何通信,而是先去关注它们是如何看到同一份资源的。

那么在进程通信之前,如何做到让进程 "先看到同一份资源" 呢?

资源的不同,决定了不同种类的通信方式! 而管道,就是提供共享资源的一种手段。

我们知道,文件在内存和磁盘之间来回切换是非常耗时的,因此进程间通信大多都是内存级别的。

即在内存内部重建一块 "小区域" 进行通信,示意图如下:

对我们来说,我们 echo 一个 hello,写到文件中,实际上这就算通信了

但是我们要讨论的不是这种通信!我们讨论的是内存级的通信!

3、管道通信的原理 

 我们在前几章中学了文件描述符 (fd) 的知识点,我们将其系统中存在的匿名管道相结合:

 首先,一个进程维护自己进程对应的文件描述符表file_struct,而 file_struct 中有对应的数组。

数字里存的是 struct file* fd_array[],这里面存的就是打开文件的文件指针。

其中 0,1,2 被默认占用,这个在之前我们也做过讲解,对应 stdin, stdout, stdin,这里不再赘述。

如果我们今天打开一个文件,OS 为了管理文件,需要将磁盘中的文件的属性信息加载到内存里。

对该文件形成 struct file,包含了文件的所有属性,对应了文件的:

  • ① 操作方法 
  • ② file 自己内部的缓冲区

如果我们让该进程 fork 创建一个子进程,

在做拷贝时是不需要将 struct file 本身给子进程拷贝一份的。

创建子进程 task_struct 和 file_struct 是需要被拷贝的,但是 struct file 是不需要的。

                                "创建进程,和我文件有什么关系?"

这也就是为什么我们创建 fork 子进程之后,让父子打印时,都会像同一个显示器打印的原因。

 结论:struct file 一定能找到对应缓冲区的操作方法和 file 自己内部缓冲区。

4、管道通信的特点 

 如何做到让不同的进程,看到同一份资源的呢?---fork让子进程继承的能够让具有血缘关系的进程进行进程间通信- 常用于父子进程

输出型参数,期望通过调用它,得到被打的文件fd

特点:

  1. 管道是用来进行具有血缘关系的进程进性进程间通信-- 常用于父子通信
  2. 管道具有通过让进程间协同,提供了访问控制!
  3. 管道提供的是面向流式的通信服务 -- 面向字节流 -- 协议
  4. 管道是基丁文件的,文件的生命周期是随进程的,管道的生命周期是随进程的!
  5. 管道是单向通信的 ,就是半双工通信的一种特殊情况

其中上面第二点理解:

  • 写快,读慢,写满不能在写了
  • 写慢,读快,管道没有数据的时候,读必须等待
  • 写关,读0,标识读到了文件结尾
  • 读关,写继续写,0S终止写进程

管道是一个文件 - -读取 -- 具有访问控制
显示器也是一个文件,父子同时往显示器写入的时候,有没有说·个会等另·个的情况呢?

--缺乏访问控制 

5、 系统调用:pipe 接口

Linux 给我们提供了 pipe 接口,只需调一下 pipe 就会在底层自动把文件以读和写的方式打开。

你会得到两个 fd,并且会被写进 pipefd[2] 数组中:

#include <unistd.h>
int pipe(int pipefd[2]);   // 数组中分别存储第一次 O_RDONLY 和 O_WRONLY

你可以理解为:pipe 内部封装了 open,并且它 open 了两次:

  • 第一次 open:以 O_RDONLY 读的方式打开
  • 第二次 open:以 O_WRONLY 写的方式打开

最后,把读写 fd 分别放在 pipefd 数组的 0 下标和 1 下标中,这就帮你创建了一个共享文件。

并且别忘了 pipe 可是系统调用,创建文件时就在内核中将文件类型初始化 i_pipe,

让它指向的是一个管道文件,指向管道信息,也就不用和磁盘产生关联了。

当父进程没有写入的时候,子进程在等,所以父进程写入之后,

子进程才能 read(会返回)到数据,子进程打印读取数据要以父进程的节奏为主。

思考:父进程和子进程读写的时候(向显示器写入也是文件),是有一定顺序性的。父子进程各自 printf 的时候,会有顺序吗?

答案是不会。管道内部没有数据,reader 就必须阻塞等待(read),管道内部如果数据被写满,此时 writer 就必须阻塞等待(write),等管道有数据。

完全乱序的地方就是缺乏访问控制,管道内部自带访问控制机制。
最后帮助大家理解管道,准备了一个程序,可以自行尝试理解:

makefile:

mypipe:mypipe.cc
	g++ -o $@ $^ -std=c++11 #-DDEBUG
.PHONY:clean
clean:
	rm -f mypipe

 mypipe.cc:

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

// 为什么不定义全局buffer来进行通信呢?? 因为有写时拷贝的存在,无法更改通信!

int main()
{
    // 1. 创建管道
    int pipefd[2] = {0}; // pipefd[0(嘴巴,读书)]: 读端 , pipefd[1(钢笔,写)]: 写端
    int n = pipe(pipefd);
    assert(n != -1); // debug assert, release assert
    (void)n;

#ifdef DEBUG
    cout << "pipefd[0]: " << pipefd[0] << endl; // 3
    cout << "pipefd[1]: " << pipefd[1] << endl; // 4
#endif
    // 2. 创建子进程
    pid_t id = fork();
    assert(id != -1);
    if (id == 0)
    {
        //子进程 - 读
        // 3. 构建单向通信的信道,父进程写入,子进程读取
        // 3.1 关闭子进程不需要的fd
        close(pipefd[1]);
        char buffer[1024 * 8];
        while (true)
        {
            // sleep(20);
            // 写入的一方,fd没有关闭,如果有数据,就读,没有数据就等
            // 写入的一方,fd关闭, 读取的一方,read会返回0,表示读到了文件的结尾!
            ssize_t s = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (s > 0)
            {
                buffer[s] = 0;
                cout << "child get a message[" << getpid() << "] Father# " << buffer << endl;
            }
            else if(s == 0)
            {
                cout << "writer quit(father), me quit!!!" << endl;
                break;
            }
        }
        // close(pipefd[0]);
        exit(0);
    }
    //父进程 - 写
    // 3. 构建单向通信的信道
    // 3.1 关闭父进程不需要的fd
    close(pipefd[0]);
    string message = "我是父进程,我正在给你发消息";
    int count = 0;
    char send_buffer[1024 * 8];
    while (true)
    {
        // 3.2 构建一个变化的字符串
        snprintf(send_buffer, sizeof(send_buffer), "%s[%d] : %d",
                 message.c_str(), getpid(), count++);
        // 3.3 写入
        write(pipefd[1], send_buffer, strlen(send_buffer));
        // 3.4 故意sleep
        sleep(1);
        cout << count << endl;
        if (count == 5){
            cout << "writer quit(father)" << endl;
            break;
        }
    }
    close(pipefd[1]);
    pid_t ret = waitpid(id, nullptr, 0);
    cout << "id : " << id << " ret: " << ret <<endl;
    assert(ret > 0); 
    (void)ret;

    return 0;
}

运行结果:

感谢阅读!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/311735.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

极客时间-读多写少型缓存设计

背景 内容是极客时间-徐长龙老师的高并发系统实战课的个人学习笔记&#xff0c;欢迎大家学习&#xff01;https://time.geekbang.org/column/article/596644 总览内容如下&#xff1a; 缓存性价比 一般来说&#xff0c;只有热点数据放到缓存才更有价值 数据量查询频率命中…

2000-2021年全国各省环境相关指标数据(890+指标)

2000-2021年全国各省环境相关指标数据&#xff08;890指标&#xff09; 1、指标时间&#xff1a;2000-2021年 2、范围&#xff1a;31省市 3、来源&#xff1a;2001-2022年环境统计年鉴 4、指标&#xff1a;工业废水排放总量、工业废水排放达标量、工业废水处理量、化学需氧…

golang 生成一年的周数

// GetWeekTimeCycleForGBT74082005 获取星期周期 中华人民共和国国家标准 GB/T 7408-2005 // 参数 year 年份 GB/T 7408-2005 func GetWeekTimeCycleForGBT74082005(year int) (*[]TimeCycle, error) {var yearstart time.Time //当年最开始一天var yearend time.Time //当年…

Python知识点(史上最全)

Python期末考试知识点&#xff08;史上最全&#xff09; python简介 Python是一种解释型语言 Python使用缩进对齐组织代码执行&#xff0c;所以没有缩进的代码&#xff0c;都会在载入时自动执行 数据类型&#xff1a;整形 int 无限大 浮点型 float…

Javaweb之SpringBootWeb案例开发规范的详细解析

1.2 开发规范 了解完需求也完成了环境搭建了&#xff0c;我们下面开始学习开发的一些规范。 开发规范我们主要从以下几方面介绍&#xff1a; 1、开发规范-REST 我们的案例是基于当前最为主流的前后端分离模式进行开发。 在前后端分离的开发模式中&#xff0c;前后端开发人员…

Vue3 父事件覆盖子事件,Vue2 的 v-on=“$listeners“ 的替代方案

在 Vue3 中&#xff0c;$listeners 被删除 子组件代码&#xff0c;需要特别注意的是事件名为 on 开头&#xff0c;例如 onBack。不确定的可以通过给父组件传递 事件或属性&#xff0c;再打印子组件的 attrs useAttrs()&#xff0c;来确定传值 // template v-bind"newA…

Netty-Netty组件了解

EventLoop 和 EventLoopGroup 回想一下我们在 NIO 中是如何处理我们关心的事件的&#xff1f;在一个 while 循环中 select 出事 件&#xff0c;然后依次处理每种事件。我们可以把它称为事件循环&#xff0c;这就是 EventLoop 。 interface io.netty.channel. EventLoo…

【自学笔记】01Java基础-08Java常用API:04包装类

记录Java基础-常用API-有关时间日期的类。 1 包装类 其实就是8种基本数据类型对应的引用类型&#xff0c;因为基本数据类型不能直接参与面向对象编程。具有将基本数据类型转换为对象的功能&#xff0c;并且实现了多种接口&#xff0c;支持集合框架和泛型。 包装类的主要特点和…

记录汇川:H5U与Fctory IO测试8

主程序&#xff1a; 子程序&#xff1a; IO映射 子程序&#xff1a; 出料程序 子程序&#xff1a; 重量程序 子程序&#xff1a; 自动程序 Fctory IO配置&#xff1a; HMI配置 实际动作如下&#xff1a; Fctory IO测试8

【一】创建Python TK GUI窗口,并简单设置窗口

文章目录 背景系统环境开始一个简单GUI启动一个GUI窗口&#xff08;不完成功能&#xff09;简单配置GUI窗口&#xff08;大小、位置、图标&#xff09; 运行示例 背景 这是一个系列文章。下一篇【【二】为Python Tk GUI窗口添加一些组件和绑定一些组件事件】 使用pyth…

AIGC大模型必备知识——LLM ,你知道它是如何训练的吗?小白必读深度好文

Look&#xff01;&#x1f440;我们的大模型商业化落地产品&#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;关注Free三天集训营助教在线为您火热答疑&#x1f469;&#x1f3fc;‍&#x1f3eb; 近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域经历了令人瞩目…

FineBI实战项目一(17):热门商品Top10分析开发

点击新建组件&#xff0c;创建热门商品Top10组件。 选择柱状图&#xff0c;拖拽cnt&#xff08;总数&#xff09;到横轴&#xff0c;拖拽goodName到纵轴。 选择排序规则。 修改横轴和纵轴的标签名称 切换到仪表板&#xff0c;拖拽组件到仪表板 效果如下&#xff1a;

今天去面一个点工,HR要我会数据库,Linux还有Python,这合理吗

软件测试出路在哪&#xff1f; 业务编程&#xff01;&#xff01; 1、软件测试的变化趋势 变化趋势1&#xff1a; 功能测试是核心&#xff0c;但是价值降低 目前测试这个行业&#xff0c;还是有大量的点工。但是行业的进步&#xff0c;技术的创新&#xff0c;导致了企业的需求…

mapper向mapper.xml传参中文时的乱码问题

1.起因&#xff1a; 在idea中进行模糊查询传参时&#xff0c;发现在idea中查中文查不出记录&#xff0c;在navicate中可以查出来。 2.猜测&#xff1a; 1.idea中的编码问题导致的乱码。 2.idea和navicate的编码一致性导致的乱码。 3.mapper向mapper.xml传参后出现乱码。 3.解…

「 典型安全漏洞系列 」02.SQL注入详解

引言&#xff1a;SQL注入是一个老生常谈且又非常重要的漏洞&#xff0c;导致许多热点的数据泄露事件。尽管学习起来相对简单&#xff0c;但它可能用于某些高危漏洞的利用。这使得它成为初学者的兴趣点&#xff0c;甚至对于更有经验的用户来说&#xff0c;SQL注入也是基本知识。…

快速打通 Vue 3(四):标签的 ref 属性与 Vue3 生命周期

很激动进入了 Vue 3 的学习&#xff0c;作为一个已经上线了三年多的框架&#xff0c;很多项目都开始使用 Vue 3 来编写了 这一组文章主要聚焦于 Vue 3 的新技术和新特性 如果想要学习基础的 Vue 语法可以看我专栏中的其他博客 Vue&#xff08;一&#xff09;&#xff1a;Vue 入…

【数据结构】--二叉树递归题记

最近写了几道关于二叉树的剑指offer题&#xff0c;和小伙伴们分享一下心得。 &#x1f308;对称的二叉树 请实现一个函数&#xff0c;用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样&#xff0c;那么它是对称的。 思路分析&#xff1a; 对于二叉树的问题来说肯…

谷达冠楠:抖音开网店创业怎么做

随着互联网的发展&#xff0c;越来越多的人选择在网上创业。而抖音作为目前最火的短视频平台之一&#xff0c;也成为了许多人开网店的首选。那么&#xff0c;如何在抖音上开网店创业呢?下面就来详细介绍一下。 第一步&#xff1a;注册账号 首先&#xff0c;你需要在抖音上注册…

登录模块的实现

一.前期的准备工作 1.页面的布局 (1)表单的校验: 利用element-ui提供的文档绑定rules规则后实现校验 (2)跨域的配置 &#xff1a; 利用proxy代理来解决跨域的问题 (3)axios拦截器的配置 两个点:1. 在请求拦截的成功回调中,如果token,因为调用其它的接口需要token才能调取。 在请…

【排序】对各种排序的总结

文章目录 前言1. 排序算法的复杂度及稳定性分析2. 排序算法的性能测试2.1 重复率较低的随机值排序测试2.2 重复率较高的随机值排序测试 前言 本篇是基于我这几篇博客做的一个总结&#xff1a; 《简单排序》&#xff08;含&#xff1a;冒泡排序&#xff0c;直接插入排序&#x…