进程间通信基础知识【Linux】——上篇
目录
一,理解进程之间的通信
1. 进程间通信目的
2. 进程间通信的技术背景
3,常见的进程间通信
二,管道
1. 尝试建立一个管道
管道的特点:
管道提供的访问控制:
2. 扩展:进程池
阶段一: 创建多个子进程
阶段二:构建命令方法
ProcessPool.cpp
task.hpp
下一期:进程通信基础知识
结语
一,理解进程之间的通信
首先,系统在设计时,秉持这相互独立的原则,因此要想实现进程之间的通信是比较困难的。而进程之间的通信本质上是:不同的进程能访问同一份数据。
1. 进程间通信目的
2. 进程间通信的技术背景
1)进程是具有独立性的,虚拟地址 + 页表 保证进程之间的独立性 (内核中数据结构 , 代码以及数据)
2)通信成本比较高。
3,常见的进程间通信
1. Linux系统——管道
2. SystemV——单机通信(多进程)
3. posix——网络通信
二,管道
而这个 “ 管道 ”虽然是一个文件,但它不向硬盘写入,管道里的数据是内存级的临时数据。
fd 这个变量就是我们父进程创建的那个pipefile(管道文件描述符存储数组),在经过pipe创建后,将管道的文件描述符填入fd数组中。
1. 尝试建立一个管道
#include <iostream>
#include <unistd.h>
#include <string>
#include <assert.h>
#include <sys/types.h>
using namespace std;
int main()
{// 用数组记录,读写端int pipefd[2] = {0}; // pipefd[0],是读端,0就如一张口;pipefd[1],是写端,1就如一只笔。int ret = pipe(pipefd);assert(ret != -1);pid_t pd = fork();// 假设父进程需要:写。if (pd == 0){ // 子进程创建成功// 子进程则需要的是:读close(pipefd[1]);cout << "chail success" << endl;char red[1024];// 循环接收并打印数据while (1){read(pipefd[0], red, sizeof (red) -1);cout << red << "### " << "我是子进程,pid:" << getpid() << "父进程: " << getppid() << endl;}exit(1);}// 父进程, 功能:写close(pipefd[0]);string base_str = "我是父进程, 正在给你发消息:";char buffer[1024]; // 输出缓冲区int count = 0;while (1) // 不断地向子进程发送数据{// 向缓冲区里,不断输入变换的消息int len = snprintf(buffer, sizeof buffer, "%s pid = [%d], 消息数量: %d", base_str.c_str(), getpid(), count++);// 向管道写入write(pipefd[1], buffer, len);sleep(1); // 一秒秒的刷新} return 0;
}
发现现象:
我们是否注意到,在父进程写数据时,是每隔一秒写一份到管道,而我们没有限制子进程打印的间隔,但从结果来看,向管道内取数据(子进程打印过程)似乎受到了什么控制。
我们尝试将父进程的写入间隔设置为默认0,将子进程的读出时间设置为10秒一次读。结果上,我们能看到父进程在写入到一定次数后,停止了写入;子进程经过10秒后,会打印一大堆数据,直到打印完为止。从这个现象我们可以发现:
管道提供访问控制。管道本质上也是一个文件,那么也有其最大容量,当即将满时,暂停写入,等待空间;当管道为空时,读操作将进行等待,等待数据写入。
管道的特点:
1. 管道是用来父子关系的通信管道,是具有继承性的。
2. 管道可以提供进程间通信,提供访问控制。
3. 管道提供面向流试的通信服务(字节流——需要协议进行数据区分,后面我们再聊)。
4. 管道是基于文件的,文件的生命周期是随进程的,因此管道的生命周期也是随进程的。
5. 管道是单向通信,属于半双工通信的一种(半双工意思是永远只有一方在写,一方在读;全双工则可以双方在写,双方在读)
管道提供的访问控制:
2. 扩展:进程池
相关知识:
1.)unin32_t 类型
uint32_t是一个无符号32位整数类型,其意义在于表示一个无符号的32位整数,范围为0到4294967295。使用uint32_t类型可以确保数据的范围和位数符合要求,同时提高代码的可移植性和可读性。这样不管在任何一种型号的机器上sizeof取得的字节数都是4字节。ssize_t类型表示有符号的大小类型,通常用于表示某些系统调用的返回值或参数。它的大小通常与机器的字长相同,即32位机器上为4字节,64位机器上为8字节。ssize_t类型通常用于表示读取或写入的字节数。
2.)functiaonal 头文件
头文件 functional 是 C++ 标准库中的一个头文件,它包含了一些模板类和函数对象,用于支持函数式编程和泛型编程。这些类和函数对象可以帮助程序员编写更加灵活和抽象的代码,提高代码的可读性和可维护性。它还提供了一些算法和函数,如 std::function、std::bind、std::mem_fn 等,用于处理函数对象和函数指针,以及实现函数的组合、绑定和适配等功能。
因此,头文件 functional 的意义在于为 C++ 程序员提供了一些强大的工具,帮助他们更好地利用函数式编程和泛型编程的特性,提高代码的质量和效率。
首先什么是进程池??
通俗的理解为,子进程替父进程进行数据处理。
父进程为了在主程序中完成任务,需要让子进程去处理数据,整理,整合数据,但为了提高创建父进程的效率,在父进程还未向子进程派发任务前,提前创建多个子进程,然后通过管道进行命令传递。
阶段一: 创建多个子进程
通过for循环创建多个子进程,并通过管道联系!
阶段二:构建命令方法
我们将方法单独一个文件,向管道中读取命令信息,然后根据命令信息调用对应的方法。
ProcessPool.cpp
#include <iostream>
#include <assert.h>
#include <vector>
#include <unistd.h>
#include "Task.hpp" //导入的方法文件
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;#define NAM_OF_PRO 5// 对命令进行处理
int waitcommand(int fd, bool& quit)
{uint32_t command = 0; // 取个32位的类型,用来获取4字节的数据ssize_t byte = read(fd, &command, sizeof command);// 如果父进程没有发送 if (byte == 0) {quit = true; // 命令管道开启 //你将他开启了return 0;}// 检测是否是4字节的命令消息1assert(byte == sizeof (uint32_t));return command;
}int main(){ // 阶段一 ; 创建多个子进程// 放pid 与 创建时所在的端口——fdvector<pair<pid_t, int>> slots; // slots 位置的意思 // 创建多个进程for (int i = 0; i < NAM_OF_PRO; i++){// 建立管道int pipefil[2] = {0};int ret = pipe(pipefil);assert(ret != -1); // 管道检测// 创建子进程pid_t fd = fork();if (fd == 0) // 子进程{// 关闭写端close(pipefil[1]);while (1){ // 处理子进程任务//未接收数据,子进程进入阻塞状态// 阶段二:为子进程设置任务bool quit = false; // 管道是否被使用int k = waitcommand(pipefil[0], quit); // 进入等待命令函数// cout << "K:" << k << " quit " << quit << endl;if (quit)break; // 目的:当我们开始关闭管道时,read()会读取0字节,这时让子进程退出tasklist[k](); // 利用函数容器直接调用,方法函数} exit(1);}// 父进程,关闭读端close(pipefil[0]);// 同时记录子进程,pid数据slots.push_back({fd, pipefil[1]});}// 阶段二: 父进程向子进程传递命令信息// 要想达到子进程接受命令较合理——负载均衡。srand((unsigned)time(0) ^ getpid() * 233333); // 位运算与相乘2333333,目的是:让数据更随机uint32_t command = 0;Taskloading(); // 对方法进行加载while (true){cout << "###########################################################\n";cout << "## 0. 退出 1. 查看方法表 2. 输入命令 #############\n";cout << "###########################################################\n";cout << "请输入:";cin >> command;//你往管道里面写数据的逻辑在哪里,定位到那里先//子进程正常执行了,你的问题是什么 if (command == 1 ){print_task();// continue;} else if ( command == 2){cout << "Please imput command : ";cin >> command;if (!(command >= 0 && command < tasklist.size())){cout << "无效命令" << endl;continue;}size_t n = rand() % slots.size(); // 随机数选择子进程ssize_t ret = write(slots[n].second, &command, sizeof command);if ( ret != sizeof command){cout << " write fail " << endl;}else{cout << " write success " << " child pid:[" << slots[n].first << "] " << "执行:"<< command << " " << task_inf[command]<< endl;}}else if (command == 0){cout << "退出成功" << endl;break;}else{cout << "无效命令,请重新输入" << endl;}sleep(1);}// 完成分配后,关闭管道for (const auto& e : slots){close(e.second);}// 回收子进程for (const auto& e : slots){waitpid(e.first, nullptr, 0); // 由于auto只遍历一次,所以不能用轮询回收}return 0;}
task.hpp
#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <unistd.h>using namespace std;typedef function<void()> func;vector<func> tasklist; // 记录方法数
unordered_map<int, string> task_inf; // 记录方法信息。void readMySQL()
{cout << " process : " << getpid() << " 访问数据库";
}void val()
{cout << " process : " << getpid() << " 进行数据运算";
}void save()
{cout << " process : " << getpid() << " 进行数据持久化";
}void cal()
{cout << " process : " << getpid() << " 进行数据加密";
}// 加载任务表
void Taskloading()
{task_inf.insert({tasklist.size(), "readMySQL"});tasklist.push_back(readMySQL);task_inf.insert({tasklist.size(), "val"});tasklist.push_back(val);task_inf.insert({tasklist.size(), "save"});tasklist.push_back(save);task_inf.insert({tasklist.size(), "cal"});tasklist.push_back(cal);cout << "方法加载成功!" << endl;
}// 打印方法表
void print_task()
{for (const auto& e : task_inf){cout << e.first << " " << e.second << endl;}
}int tasksize()
{return tasklist.size();
}
我给大家留了个坑,不知道大家有没有发现呢? 我们在最后的时候调用不了函数
解析: 是进程的独立性导致的。 我们知道我们在已经对方法容器进行了加载,但是在fork之后父进程的加载,父进程进行了实时拷贝,子进程的tasklist任然是未加载状态,这时你就会问了,既然子进程访问的是一个空容器,那我们不就是非法访问了吗?? 是的,我们没有看到报错是因为我们在父进程,子进程的报错不会导致父进程退出,我们通过 查看进程状态表即可发现,由于子进程的非法访问,导致子进程崩溃,成为了僵尸进程。
解决方法:
1, fork前,进行方法表加载。
2. 每个子进程都加载。
分析:父子进程,对共享的全局变量,都只是只读的权限,修改则要写实拷贝,因此,要么全局变量在fork之前就数据加载好;要么,在每个子进程都加载一份方法表。如果是我,我会一开始就加载好,这样就只要保存一份数据。
下一期:进程通信基础知识
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。
相关文章:

进程间通信基础知识【Linux】——上篇
目录 一,理解进程之间的通信 1. 进程间通信目的 2. 进程间通信的技术背景 3,常见的进程间通信 二,管道 1. 尝试建立一个管道 管道的特点: 管道提供的访问控制: 2. 扩展:进程池 阶段一:…...

OpenSSH(CVE-2023-38408)OpenSsh9.5一键升级修复
yum install -y git cd /root git clone https://gitee.com/qqmiller/openssh-9.5p1-.x86_64.git cd openssh-9.5p1-.x86_64/ bash openssh_update.sh重启sshd: systemctl restart sshd 查看sshd状态: systemctl status sshd 重要的是按此操作升级完成…...

10.30 作业 C++
设计一个Per类,类中包含私有成员:姓名、年龄、指针成员身高、体重,再设计一个Stu类,类中包含私有成员:成绩、Per类对象p1,设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespace std;clas…...

Python开发运维:PyMongo 连接操作 MongoDB
目录 一、理论 1.PyMongo模块 2.Mongo Shell 二、实验 1. Windows11安装MongoDB 7.0.4 2.Windows11安装MongoDB Shell 2.1.0 3.PyMongo 连接 MongoDB(无密码方式) 4.PyMongo 连接 MongoDB(有密码方式) 5.PyMongo 操作 Mo…...
【Github】本地管理github分支
本地管理github分支 学习一些开发tips。以下是万能的GPT教我的: 以下是一套基本的本地管理 GitHub 仓库的指令集。在执行这些指令之前,请确保已经在你的本地机器上安装了 Git 工具,并且已经在 GitHub 上创建了一个仓库。 克隆仓库࿱…...

Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap
在 Spring Boot 项目中,我们经常使用 YAML 文件来配置应用程序的属性。在这篇博客中,我将模拟如何在 Java 的 Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap。 1. 介绍 YAML(YAML Aint Markup Language)是一种人类…...

Python正则表达式:match()和search()函数全面解读
更多资料获取 📚 个人网站:ipengtao.com 在Python中,正则表达式是强大的工具,能够用于文本匹配、搜索和替换。re模块提供了许多函数来处理正则表达式,其中match()和search()是两个常用的函数。本文将深入探讨这两个函…...

AIGC ChatGPT4总结Linux Shell命令集合
在Linux中,Shell命令的数量非常庞大,因为Linux提供了各种各样的命令来处理系统任务。这些命令包括GNU核心工具集、系统命令、shell内置命令以及通过安装获得的第三方应用程序命令。以下是一些常见的Linux命令分类及其示例,但请注意,这不是一个全面的列表,因为列出所有命令…...
力扣labuladong——一刷day61
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣865. 具有所有最深节点的最小子树二、力扣1123. 最深叶节点的最近公共祖先三、力扣1026. 节点与其祖先之间的最大差值四、力扣1120. 子树的最大平均值 …...

nacos配置变更导致logback日志异常
问题背景: 线上的服务突然内存爆满,查服务器突然发现,日志全部打印到了/tmp/tomcat.xxx.port目录下,后来对应操作时间,和nacos修改配置是同一时间发生的,但是疑惑的点是,nacos配置变更为什么会引起logback的…...

【spring(五)】SpringMvc总结 SSM整合流程
目录 一、SpringMVC简介: 二、SpringMVC快速入门: 三、SpringMVC bean的管理:⭐ ①配置bean ②扫描bean 四、SpringMVC配置类:⭐ 五、SpringMVC 请求与响应 六、SpringMVC REST风格 七、SSM整合 异常处理: 八、…...

1、windows10系统下Qt5.12.0与卸载
一、安装包下载 1、Qt社区下载 https://download.qt.io/archive/qt/5.12/5.12.10/qt-opensource-windows-x86-5.12.10.exe 2、百度网盘下载 链接:百度网盘 请输入提取码 3、Qt官网下载: Try Qt | 开发应用程序和嵌入式系统 | Qt 二、安装提示 下…...

WebGL/threeJS面试题扫描与总结
什么是 WebGL?什么是 Three.js?请解释three.js中的WebGL和Canvas的区别? WebGL(全写Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个…...
Qt connect()方法Qt::ConnectionType
connect() Qt,绑定信号和槽原型: static QMetaObject::Connection connect(const QObject *sender, const char *signal,const QObject *receiver, const char *member, Qt::ConnectionType Qt::AutoConnection);static QMetaObject::Connection conn…...
HIVE SQL时间函数
目录 current_timestamp()获取当前时间unix_timestamp()获取当前时区的UNIX时间戳from_unixtime()时间戳转日期函数unix_timestamp(string date)日期转时间戳函数提取日期中的年月日时分秒weekofyear (string date)日期转周函数日期比较函数datediff(string enddate, string st…...
linux磁盘的LVM、交换分区以及文件系统
目录 逻辑卷LVM LVM管理 LVM特点 LVM的制作 创建物理卷 创建卷组 创建逻辑卷 格式化文件系统 挂载逻辑卷 LVM的扩容 添加硬盘做物理卷 卷组扩容 扩容逻辑卷 给文件系统扩容 LVM移除 LVM的缩容 交换分区 查看当前交换分区:free Swap:虚…...
【HDFS】ActiveNamenodeResolver#getNamespaces 方法调用点梳理
获取所有的注册在router里的active状态的集群。 /*** Get a list of all namespaces that are registered and active in the* federation.** @return List of name spaces in the federation* @throws IOException Throws exception if the namespace list is not* av…...

算法—双指针
双指针算法可以帮忙把时间复杂度降低一个维度,即原本O(n2)降为O(n);将O(n)降为O(1) 移动零 移动零 题目解析 将所有0移动到末尾保持非0元素相对顺序对数组进行原地操作(不开辟额外空间) 算法原理 数组…...

[Oracle]编写程序,键盘输入n,计算1+前n项之和。测试案例:输入:10 输出:22.47
编写程序,键盘输入n,计算1前n项之和。 测试案例: 输入:10 输出:22.47 代码如下: set serveroutput on declare v_sum number:0;v_n number;beginv_n:&n;for i in 1..v_n loopv_sum:v_sumsqrt(i); end loop; d…...

【视觉SLAM十四讲学习笔记】第三讲——旋转向量和欧拉角
专栏系列文章如下: 【视觉SLAM十四讲学习笔记】第一讲——SLAM介绍 【视觉SLAM十四讲学习笔记】第二讲——初识SLAM 【视觉SLAM十四讲学习笔记】第三讲——旋转矩阵 本章将介绍视觉SLAM的基本问题之一:如何描述刚体在三维空间中的运动? 旋转向…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...

【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项
一、条形码识别改名使用教程 打开软件并选择处理模式:打开软件后,根据要处理的文件类型,选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件,就选择 “PDF 识别模式”;若是处理图片文件&…...

【Vue】scoped+组件通信+props校验
【scoped作用及原理】 【作用】 默认写在组件中style的样式会全局生效, 因此很容易造成多个组件之间的样式冲突问题 故而可以给组件加上scoped 属性, 令样式只作用于当前组件的标签 作用:防止不同vue组件样式污染 【原理】 给组件加上scoped 属性后…...