进程间通信 管道

前言

ubuntu系统的默认用户名不为root的解决方案(但是不建议):轻量应用服务器 常见问题-文档中心-腾讯云 (tencent.com)

进程间通信的基本概念

进程间通信目的:进程间也是需要协同的,比如数据传输、资源共享、通知事件、进程控制

进程间通信的前提:让不同进程看到同一份OS中的资源(一段内存) 

  1. 一定是某个进程提出了进程间通信的请求,OS才会创建一个共享资源
  2. 为了防止进程在通信时直接访问OS,OS会提供很多系统调用接口
  3. OS创建的共享资源的不同 + OS提供的系统调用接口的不同 = 进程间通信会有不同的种类 

 注意事项:

1、进程间能通信不等于能一直通信,fork函数子进程继承父进程不属于进程间通信

2、进程间通信的成本可能会稍微高一点

进程间通信依赖的标准:system V标准(主要用于本地通信) 和posix标准

system V标准规定的三种进程间通信的方案:消息队列、共享内存、信号量

管道

基本概念:System V 标准中提供了多种 IPC 机制,如消息队列、共享内存和信号量,但是使用这些 IPC 机制需要考虑很多细节问题(例如缓冲区大小、同步与互斥等),并且需要编写复杂的代码来实现,而管道只需一条简单命令即可创建,并且它们支持两个相关联地运行在同一系统上地程序之间互相传输信息(管道是最初人们实现进程间通信的一种方式)

问题:为什么父子进程会向同一个显示器终端打印数据?

解释:进行写操作时,父子进程都会向同一个内核级文件缓冲区中写入(内核级文件缓冲区不属于任何文件)当操作系统定期刷新时会将该缓冲区中的内容刷新到指定的文件中(在这里就是显示器文件)

问题:进程怎么做到默认打开三个标准流0、1、2?

解释:因为所有的进程都是bash的子进程,当bash打开(指向这三个流的文件,具体细节不再描述)了,bash的子进程也就打开了

问题:为什么子进程主动close(0或1或2)不影响父进程继续使用显示器文件呢?

解释:struct file中存在一个内存级的引用计数,父子进程同时指向一个struct file则该引用计数为2,close子进程的某个标准流文件时,只会将该引用计数减一,父进程依然可以访问(file - > ref_count--; if(ref_count == 0)才会释放文件资源)

问题:什么是管道文件?

解释:内核级文件缓冲区(重新设计后的) + struct file,父子进程一个负责向内核级文件缓冲区中写,另一个读取内核级文件缓冲区中的内容就形成了进程间通信的定义(让不同的进程看到同一份OS中的资源,文件系统也属于OS),此外为了保证父子进程间通信的合理性,管道文件只允许单向通信,同时读取会发生数据紊乱

补充:为了提高进程间通信的效率,避免写入文件缓冲区后还要向磁盘文件中刷新,所以OS设计者基于原来内核级文件缓冲区的代码,在OS中重新设计了一个不需要向磁盘中定时刷新的内核级文件缓冲区 

问题:如何实现父进程读文件,子进程写文件?

解释:父进程仍然打开3号文件描述符close(4),子进程仍然打开4号文件描述符close(3)

问题:父子既然要关闭不需要的fd,为何之前还要打开?可以不关闭吗?

解释:①为了让子进程继承,通过继承后不用了再关闭的这种方式形成的进程间通信是由设计者深思熟虑后的结果(父进程只打开一个3后续子进程还要再关闭3再打开4,还不如父子进程都打开3和4按照实际情况再进行关闭,前者也可以但是后者更简单)可以不关闭但是可能会造成父子进程同时写入,所以建议关闭,同时由于存放文件描述符的是一个数组,数组是有大小范围的,所以如果为了保证通信的单向性子进程让某个文件描述符空闲,如果还有其它情况造成的文件描述符在数组中处于空闲状态,就会造成文件描述符泄漏

匿名管道

pipe函数

函数原型:int pipe(int pipefd[2]);

包含头文件:<unistd.h>

参数:输出型参数,是一个由两个整数构成的数组,第一个元素表示读端的文件描述符,第二个元素表示写端的文件描述符,写端和读端的文件描述符由OS自行填写

返回值:调用成功返回0,否则返回-1

功能:在OS中创建一个用于进程间通信的没有名字的内核级文件缓冲区,即匿名管道

注意事项:

1、pipe函数的底层是open函数,只不过这里不需要提供文件路径、文件名以及初始权限

2、如果想要双向通信可以使用两个管道

3、将pipe创建的内核级文件缓冲区叫做管道,是因为它在本质上还是一个内核级文件缓冲区,只不过正常情况下一个进程对文件进行写的时候就是写入内核级文件缓冲区然后由OS负责定时刷新到管道,而新建的缓冲区不会向磁盘中刷新而是刷新给进程,刷新的目的地改变了,只需将原来内核级文件缓冲区的代码稍加更改就可以实现这一功能,并且“一进一出”还符合我们日常生活中对管道的理解

cfc

1、进程进程间通信是有成本的,需要做准备工作:

//2、创建子进程
pid_t id = fork();
if(id == 0)
{
    //子进程---写端
    //3、关闭不需要的fd
    close(pipefd[0]);


    close(pipefd[1]);//完成通信后也将子进程的写端关闭
    exit(0);
}

//父进程---读端
close(pipefd[1]);

close(pipefd[0]); // 完成通信后也将子进程的读端关闭

2、进程间通信:

#include <iostream>
#include <unistd.h>
#include <cerrno>  //c++版本的errno.h
#include <cstring> //c++版本的string.h
#include <sys/wait.h>
#include <sys/types.h>
#include <string>

// 携带发送的信息
std::string getOtherMessage()
{
    // 获取要返回的信息
    static int cnt = 0; // 计数器
    std::string messageid = std::to_string(cnt);
    cnt++;                    // 每使用一次计数器就++
    pid_t self_id = getpid(); // 获取当前进程的pid
    std::string stringpid = std::to_string(self_id);

    std::string message = " my messageid is : ";
    message += messageid;
    message += " my pid is : ";
    message += stringpid; // 逐渐向要传回的string字符串中追加要返回的信息

    return message;
}

// 子进程进行写入
void ChildProcessWrite(int wfd)
{
    std::string message = "father, I am your child process!";
    while (true)
    {
        std::string info = message + getOtherMessage(); // 子进程尝试向父进程传递的所有信息
        write(wfd, info.c_str(), info.size());          // write函数传入的字符串需要是c语言格式的,c_str将string字符串变为c语言格式的字符串
        sleep(1);                                       // 让子进程写慢一点,这样父进程就不会一直读并打印在显示器上
    } // write是由操作系统提供的接口,而操作系统又是C语言编写的,所以后续学习中可能会碰到c语言的接口和c++的接口混合使用的情况
} // info最后有/0但是文件不需要

const int size = 1024; // 定义父进程可以读取的数组大小

// 父进程进行读取
void FatherProcessRead(int rfd)
{
    char inbuffer[size]; // 普通的c99标准不支持变长数组,但是这里使用的是gnb的c99标准,gun的c99标准支持变长数组
    while (true)
    {
        ssize_t n = read(rfd, inbuffer, sizeof(inbuffer)); // 因为文件不需要\0,所以读取管道中内容到缓冲区时可以少读取一个并将/0变为0
        if (n > 0)
        {
            inbuffer[n] = 0;
            std::cout << "父进程获取的消息: " << inbuffer << std::endl;
        }
    }
}

int main()
{
    // 1、创建管道
    int pipefd[2];
    int n = pipe(pipefd); // 输出型参数,rfd,wfd
    if (n != 0)
    {
        std::cerr << "errno" << errno << ":" << "errstring" << strerror(errno) << std::endl;
        return 1;
    }

    // pipefd[0]即读端fd,pipefd[1]即写端fd
    std::cout << "pipefd[0] = " << pipefd[0] << ", pipefd[1] = " << pipefd[1] << std::endl;

    sleep(1); // 便于看到管道创建成功

    // 2、创建子进程
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "子进程关闭不需要的fd,准备发消息了" << std::endl;
        sleep(1); // 便于感受到发消息的过程

        // 子进程---写端
        // 3、关闭不需要的fd
        close(pipefd[0]);

        ChildProcessWrite(pipefd[1]); // 子进程的写函数

        close(pipefd[1]); // 完成通信后也将子进程的写端关闭
        exit(0);
    }
    std::cout << "发进程关闭不需要的fd,准备收消息了" << std::endl;
    sleep(1); // 便于感受到收消息的过程

    // 父进程---读端
    close(pipefd[1]);
    FatherProcessRead(pipefd[0]); // 父进程的读函数
    close(pipefd[0]);             // 完成通信后也将子进程的读端关闭

    pid_t rid = waitpid(id, nullptr, 0);
    if (rid > 0)
    {
        std::cout << "wait child process done" << std::endl;
    }

    return 0;
}

结论:因为可以用write和read读取管道,所以管道也是文件

管道的四种情况

1、如果管道内部为空,不具备读取条件,读进程会被阻塞(wait)等到管道不为空时才会读取

2、管道被写满 && rfd不关闭也不读取:此时管道会被写满,写进程会被阻塞,等到管道不为满时才会继续写入

3、管道一直在读 && wfd关闭:读端read函数的返回值最后为0,表示读取到了文件结尾

4、rfd直接关闭 && 写端一直入:写端进程会被OS直接用13号信号杀掉(OS判断出进程异常)

管道的五种特征

1、对于匿名管道:只能用来进行具有“血缘关系”的进程间的通信,但常用于父子进程间通信

2、管道内部自带进程之间的同步机制(子进程写一条写父进程读一条(但也不绝对),管道在实现时内部做了保护,不会出现多进程同时访问共享资源导致的共享区数据不一致问题)

3、管道文件按的生命周期是随进程的

4、管道文件在通信的时候,是面向字节流的,读写次数不一定是一一匹配的(写十次一次一条,读一次一次读十条,水管一直流,但是可以选用不同的容器去接水)

5、管道的通信模式,是一种特殊的半双工模式(正常的半双工是双方都写入和接收,但同时只能有一个人写入另一个人负责接收,管道是永远只能有一个人进行写入另一个人进行接收)

~over~

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

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

相关文章

人脸图像生成(DCGAN)

一、理论基础 1.什么是深度卷积对抗网络&#xff08;Deep Convolutional Generative Adversarial Network&#xff0c;&#xff09; 深度卷积对抗网络&#xff08;Deep Convolutional Generative Adversarial Network&#xff0c;DCGAN&#xff09;是一种生成对抗网络&#xf…

计算机通信SCI期刊推荐,JCR1区,IF=6+,审稿快,无版面费!

一、期刊名称 Computer Communications 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;6 中科院分区&#xff1a;3区 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需支付$2300 三、期刊征稿…

STM32中的ICACHE是什么有什么用?如何使用?

什么是ICACHE&#xff1f; icache是一种用于缓存指令的存储器&#xff0c;其目的是提高CPU执行指令的效率。 在计算机系统中&#xff0c;icache&#xff08;指令缓存&#xff09;是处理器核心内部的一个关键组件&#xff0c;它专门用来存储最近使用过的指令。当CPU需要执行一个…

Bean的作用域

Bean的作用域 Bean的作用域是指在Spring整个框架中的某种行为模式&#xff0c;比如singleton单例作用域&#xff0c;就表示Bean在整个spring中只有一份&#xff0c;它是全局共享的&#xff0c;那么当其他人修改了这个值时&#xff0c;那么另一个人读取到的就是被修改的值 Bea…

每日OJ题_记忆化搜索②_力扣62. 不同路径(三种解法)

目录 力扣62. 不同路径 解析代码1_暴搜递归&#xff08;超时&#xff09; 解析代码2_记忆化搜索 解析代码3_动态规划 力扣62. 不同路径 62. 不同路径 难度 中等 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器…

element-ui skeleton 组件源码分享

今日简单分享 skeleton 骨架屏组件源码&#xff0c;主要从以下四个方面来讲解&#xff1a; 1、skeleton 组件的页面结构 2、skeleton 组件的属性 3、skeleton item 组件的属性 4、skeleton 组件的 slot 一、skeleton 组件的页面结构 二、skeleton 组件的属性 2.1 animate…

BS架构 数据权限--字段级权限 设计与实现

一、需求场景 1. 销售发货场景 销售出库单上 有 商品名称、发货数量、单价、总金额 等信息。 销售人员 关注 上述所有信息&#xff0c;但 仓管人员 不需要知道 单价、总金额 信息。 2. 配方、工艺保密 场景 配方研发人员 掌握核心配方&#xff0c; 但 交给车间打样、生产时…

一款免费的PDF转换工具分享

最近在吾爱上发现一款PDF免费转换工具&#xff0c;支持多种格式转换&#xff0c;试了一下&#xff0c;还不错 最重要的是免费&#xff0c;不用开会员转换&#xff0c;也没有限制&#xff08;文末有工具地址&#xff09; ps:转换完成后看一下是否符合&#xff0c;可能会有些许…

即插即用模块:Convolutional Triplet注意力模块(论文+代码)

目录 一、摘要 二、创新点总结 三、代码详解 论文&#xff1a;https://arxiv.org/pdf/2010.03045v2 代码&#xff1a;https://github. com/LandskapeAI/triplet-attention 一、摘要 由于注意机制具有在通道或空间位置之间建立相互依赖关系的能力&#xff0c;近年来在各种计…

Web功能测试之表单、搜索测试

初入职场接触功能测试老是碰到以下情况不知道怎么写测试用例&#xff1a; 一个界面很多搜索条件怎么写用例&#xff1f; 下拉框测试如何考虑测试点&#xff1f; 上传要考虑哪些验证点&#xff1f;...... 所以这篇主要是整理关于web测试之表单、搜索测试的相关要点。 一、表…

小程序(三)

十三、自定义组件 &#xff08;二&#xff09;数据方法声明位置 在js文件中 A、数据声明位置&#xff1a;data中 B、方法声明位置methods中&#xff0c;这点和普通页面不同&#xff01; Component({/*** 组件的属性列表*/properties: {},/*** 组件的初始数据*/data: {isCh…

离线使用evaluate

一、目录 步骤demorouge-n 含义 二、实现 步骤 离线使用evaluate: 1. 下载evaluate 文件&#xff1a;https://github.com/huggingface/evaluate/tree/main2. 离线使用 路径/evaluate-main/metrics/rougedemo import evaluate离线使用evaluate: 1. 下载evaluate 文件&…

# 从浅入深 学习 SpringCloud 微服务架构(十五)

从浅入深 学习 SpringCloud 微服务架构&#xff08;十五&#xff09; 一、SpringCloudStream 的概述 在实际的企业开发中&#xff0c;消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&…

Java中使用RediSearch进行高效数据检索

RediSearch是一款构建在Redis上的搜索引擎&#xff0c;它为Redis数据库提供了全文搜索、排序、过滤和聚合等高级查询功能。通过RediSearch&#xff0c;开发者能够在Redis中实现复杂的数据搜索需求&#xff0c;而无需依赖外部搜索引擎。本文将介绍如何在Java应用中集成并使用Red…

3. 多层感知机算法和异或门的 Python 实现

前面介绍过感知机算法和一些简单的 Python 实践&#xff0c;这些都是单层实现&#xff0c;感知机还可以通过叠加层来构建多层感知机。 2. 感知机算法和简单 Python 实现-CSDN博客 1. 多层感知机介绍 单层感知机只能表示线性空间&#xff0c;多层感知机就可以表示非线性空间。…

切割链表 问题的讲解和实现(带哨兵位)

一&#xff1a;题目 二&#xff1a;思路讲解 先 将小于x的放进一个新的链表&#xff0c;将≥x的也放进另一个新链表。 然后 第一个新链表的尾节点的next链接到第二个新链表的哨兵节点的next&#xff0c;因为本身不存在哨兵位。 最后 链接完成后的链表的最后一个节点的next一…

python数据分析——数据的选择和运算

数据的选择和运算 前言一、数据选择NumPy的数据选择一维数组元素提取示例 多维数组行列选择、区域选择示例 花式索引与布尔值索引布尔索引示例一示例二 花式索引示例一示例二 Pandas数据选择Series数据获取DataFrame数据获取列索引取值示例一示例二 取行方式示例loc() 方法示例…

天地图2024版正式启用!首次开放多时相影像专题,可直接查看历史影像

近日&#xff0c;国家基础地理信息中心正式发布了天地图2024版。 新版本更新2米分辨率遥感影像794万平方公里、优于1米分辨率遥感影像655万平方公里&#xff0c;在线服务2米分辨率遥感影像覆盖全部陆地国土、优于1米遥感影像覆盖陆地国土达到98%&#xff0c;同时新增多时相遥感…

航空科技:探索飞机引擎可视化技术的新视界

随着航空技术的飞速发展&#xff0c;飞机引擎作为航空器最为关键的部件之一&#xff0c;其性能直接影响到飞机的安全性、经济性和环保性。因此&#xff0c;飞机引擎可视化技术的应用日益成为航空行业研究和发展的热点。 通过图扑将复杂的飞机引擎结构和工作原理以直观、生动的…

Linux中的日志系统简介

在的Linux系统上使用的日志系统一般为rsyslogd。rsyslogd守护进程既能接收用户进程输出的日志&#xff0c;又能接收内核日志。用户进程是通过调用syslog函数生成系统日志的。该函数将日志输出到一个UNIX本地域socket类型&#xff08;AF_UNIX&#xff09;的文件/dev/log中&#…
最新文章