基于POSIX标准的Linux进程间通信

文章目录

  • 1 管道(匿名管道)
    • 1.1 管道抽象
    • 1.2 接口——`pipe`
    • 1.3 管道的特征
    • 1.4 管道的四种情况
    • 1.5 匿名管道用例
  • 2 命名管道
    • 2.1 创建一个命名管道——`mkfifo`
    • 2.2 关闭一个管道文件——unlink
    • 2.3 管道和命名管道的补充
    • 2.4 命名管道用例
  • 3 共享内存
    • 3.1 原理
    • 3.2 系统调用接口——`shmget`
      • 3.2.1 `key`
      • 3.2.2 `ftok`——返回key
      • 3.2.3 `size`
      • 3.2.4 `shmflg`
      • 3.2.5 返回值——shmid
    • 3.3 主动释放共享内存
      • 3.3.1 使用命令——`ipcrm`
      • 3.3.2 系统调用函数——`shmctl`
    • 3.4 将进程与共享内存挂接
      • 3.4.1 关联——`shmat`
      • 3.4.2 去关联——`shmdt`
    • 3.5 共享内存的特点
    • 3.6 共享内存属性 —— `chmctl`
    • 3.5 共享内存用例
  • 4 消息队列
  • 5 IPC在内核中的数据结构

进程间的通信标准

  • system V IPC
    • 消息队列、共享内存、信号量
  • POSIX IPC
  • 消息队列、共享内存、信号量、互斥量、条件变量、读写锁
  • 基于文件的通信方式——管道

1 管道(匿名管道)

当创建子进程时,进程中的files_struct同样被赋值了一份,如果此时内存中打开了一份内存级文件(该文件同样包含:cnt、inode、file_operators、缓冲区,但是该内存级文件不需要向磁盘刷新缓冲区),并且在父进程的文件描述符表中,那么子进程也会赋值也会同样打开这份文件,此时父子进程可以通过该文件进行通信

1.1 管道抽象

image-20231113233932235

image-20231113235339531

  • 管道只能进行单向通信

1.2 接口——pipe

#include <unistd.h>int pipe(int pipefd[2]);
//pipefd为输出型参数,函数通过该参数将两个文件描述符输出出来,给用户使用
//pipefd[0] : 读下标
//pipefd[1] : 写下标

1.3 管道的特征

  • 具有血缘关系的进程进行进程间通信
  • 管道只能单向通信
  • 父子进程之间是会协同的,同步与互斥 —— 保护管道文件的数据安全
  • 管道是面向字节流的
  • 管道是基于文件的,而文件的生命周期是随进程的,当进程退出时系统自动释放文件内存

查看一些操作系统的限制,例如管道的大小

ulimit -a

1.4 管道的四种情况

  1. 读写端正常,管道为空,读端阻塞
  2. 读写端正常,管道被写满,写端阻塞
  3. 读写端正常,写端关闭,读端read返回0,表示读到文件(pipe)结尾,不会被阻塞
  4. 写端正常,读端关闭,操作系统会通过信号(13: SIGPIPE)杀掉正在写入的进程

1.5 匿名管道用例

//匿名管道用例
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <sys/wait.h>using namespace std;void Writer(int write_fd) {char buffer[1023] = {'\0'};snprintf(buffer, sizeof(buffer), "%s-%d", "message, pid: ", getpid());write(write_fd, buffer, strlen(buffer));
}void Reader(int read_fd) {char buffer[1023] = {'\0'};ssize_t n = read(read_fd, buffer, sizeof(buffer));buffer[n] = '\0';cout << "child output: " <<  buffer << endl;
}int main() {int pipefd[2];int ret = pipe(pipefd);pid_t id = fork();if (id == 0) {close(pipefd[1]);Reader(pipefd[0]);}close(pipefd[0]);Writer(pipefd[1]);int wait_ret = waitpid(id, nullptr, 0);return 0;
}

2 命名管道

  • 如果两个不同的进程,打开同一个文件的时候,在内核中,操作系统只会打开一个文件
  • 管道文件
    • 内存级文件,只用文件缓冲区,不用写入到磁盘中
    • 两个进程如何知道打开的是同一份文件:使用同一个文件名

2.1 创建一个命名管道——mkfifo

  • 与文件相同,不能重复创建文件名相同的管道文件!!
  1. 命令

image-20231120170010308

image-20231120170102231

  1. 系统调用函数

image-20231120171040698

2.2 关闭一个管道文件——unlink

image-20231120171212163

2.3 管道和命名管道的补充

  1. 多个进程在通过管道通信时,删除管道文件则无法继续通信? —— 错误

    管道的生命周期随进程,本质是内核中的缓冲区,命名管道文件只是标识,用于让多个进程找到同一块缓冲区,删除后,之前已经打开管道的进程依然可以通信

  2. 命名管道的本质和匿名管道的本质相同都是内核中的一块缓冲区

2.4 命名管道用例

//命名管道用例//comm.hpp
#pragma once
#include <sys/types.h>
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>using namespace std;
const string pathname = "./myfifo";class Init {
public:Init() {mkfifo(pathname.c_str(), 0666);}~Init() {unlink(pathname.c_str());}
};//server.cc
#include "comm.hpp"
//读数据
int main() {Init init;    //创建管道文件,进程结束调用unlink销毁管道文件int fd = open(pathname.c_str(), O_RDONLY);while (true) {char buffer[1024] = {0};ssize_t n = read(fd, buffer, sizeof(buffer));if (n == 0) {cout << "write close -> read close" << endl;break;}buffer[n] = 0;cout << "[server read]$ " << buffer << endl;}close(fd);return 0;
}//client.cc
#include "comm.hpp"
//写数据
int main() {//读端关闭,发送SIGPIPE信号signal(SIGPIPE, [](int){cout << "read close -> write close" << endl;// unlink(pathname.c_str());exit(0);});int fd = open(pathname.c_str(), O_WRONLY);string input;while (true) {cout << "[client write]$ ";getline(cin, input);write(fd, input.c_str(), input.size());}close(fd);return 0;
}

3 共享内存

3.1 原理

  1. 申请内存(通过系统调用, 通过操作系统管理所有共享内存)
  2. 挂接到进程地址空间
  3. 返回首地址(虚拟地址)

image-20231120191422225

3.2 系统调用接口——shmget

//创建共享内存用例
const string pathname = "/home/dusong";
const int proj_id = 0x12345;
const int size = 4096;
int main() {key_t k = ftok(pathname.c_str(), proj_id);int shmid = shmget(k, size, IPC_CREAT|IPC_EXCL|0X666);
}

image-20231120192625794

3.2.1 key

  • 通过key标识系统中的一块共享内存
  • 对于一个已经创建好的共享内存,key在哪里? key在共享内存的描述对象中

3.2.2 ftok——返回key

image-20231120194416466

通过pathnameproj_id生成一个哈希值,作为key

  • 若返回值<0,则key获取失败

3.2.3 size

共享内存大小,单位字节

  • 共享内存的大小一般建议是4096(4Kb)的整数倍
  • 如果size为4097,操作系统会给4096*2的空间

3.2.4 shmflg

  • IPC_CREAT:单独使用时,如果申请的共享内存不存在,则创建,存在则获取并返回
  • IPC_CREAT|IPC_EXCL:如果申请的共享内存不存在,则创建,存在则出错返回,确保每次申请都是一块新的共享内存
  • IPC_EXCL不单独使用
  • 权限(八进制)

3.2.5 返回值——shmid

共享内存标识符shmid

keyshmid的区别

key:用于操作系统内标定唯一性;

shmid(shmget的返回值):只在进程内,用来表示资源的唯一性

  • 查看所有shmid:
ipcs -m 

3.3 主动释放共享内存

共享内存被删除后,则其它进程直接无法实现通信?——错误

共享内存的删除操作并非直接删除,而是拒绝后续映射,只有在当前映射链接数为0时,表示没有进程访问了,才会真正被删除

image-20231123000221166

当进程还在通过共享内存通信时,通过ipcrm删除共享内存,此时任然可以通信

3.3.1 使用命令——ipcrm

  • 使用shmid关闭共享内存
ipcrm -m [shmid]
  • 删除所有进程间通信资源
ipcrm -a

3.3.2 系统调用函数——shmctl

image-20231120234004628

  • 参数2:cmd

    • IPC_RMID:删除共享内存
    • IPC_STAT:查看属性
  • 参数3:buf

    • 删除共享内存时,置为nullptr
    • 查看属性时作为输出型参数,传入一个shmid_ds结构体(描述管理共享内存的属性的结构体)

3.4 将进程与共享内存挂接

3.4.1 关联——shmat

image-20231120232143685

参数2:shmaddr: 指定将共享内存挂接到地址空间的哪个部分,一般设为nullptr

参数3:shmflg: 权限设置, 通常设置为0

返回值:连接到虚拟地址的首地址(作为shmaddr)

  • 查看一个共享内存被进程挂接的数量:
ipcs -m
  • shmat之后nattch+1

image-202312081252594731

3.4.2 去关联——shmdt

  • 进程退出时会自动去关联

image-20231120233329871

3.5 共享内存的特点

  1. 共享内存没有同步互斥之类的保护机制,没有内容时不阻塞(管道要阻塞)
  2. 共享内存是所有进程间通信中速度最快的,因为拷贝少,对比管道,管道需要先将内容写到用户级缓冲区中,再通过write写到文件缓冲区中
  3. 共享内存内部的数据由用户自己维护

3.6 共享内存属性 —— chmctl

image-20231121130404235

const string pathname = "/home/dusong";
const int proj_id = 0x12345;
const int size = 4096;int main() {key_t k = ftok(pathname.c_str(), proj_id);int shmid = shmget(k, size, IPC_CREAT|IPC_EXCL|0X666);struct shmid_ds shmds;shmctl(shmid, IPC_STAT, &shmds);cout << shmds.shm_stgsz << << endl;   //该共享内存的大小cout << shmds.shm_prem.__key << << endl;   //keycout << shmds.shm_prem.mode << << endl;    //权限
}

3.5 共享内存用例

//comm.hpp
#pragma once
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
using namespace std;const string pathname = "./home/for_pipe/sharedmemory";
const int proj_id = 1234;
const size_t size = 4096;
const string pipepath = "./myfifo";class Shm {
public:Shm(){int ret = mkfifo(pipepath.c_str(), 0666);if (ret < 0) {cout << "make namedfifo fail"<< endl;exit(1);}if (shmid < 0) {cout << "shared_memory have created" << endl;exit(1);}}~Shm() {shmctl(shmid, IPC_RMID, nullptr);unlink(pipepath.c_str());}static int key;static int shmid;
};
int Shm::key = ftok(pathname.c_str(), proj_id);
int Shm::shmid = shmget(Shm::key, size, IPC_CREAT|IPC_EXCL|0666);
//server.cc
#include "comm.hpp"//读端
int main() {Shm shm;int fd = open(pipepath.c_str(), O_RDONLY);   //通过管道保证互斥与同步char* st = (char*)shmat(shm.shmid, nullptr, 0);char buffer[2] = {0};while(true)  {int n = read(fd, buffer, sizeof(buffer));if (n == 0) {cout << "write close -> read close" << endl;break;}cout << "[sever read]$ " << st << endl; sleep(1);}shmdt(st);
}
//client.cc
#include "comm.hpp"//写端
int main() {//默认信号处理为忽略signal(SIGPIPE, [](int){shmctl(Shm::shmid, IPC_RMID, nullptr);unlink(pipepath.c_str());cout << "read close -> write close" << endl;exit(0);});int key = ftok(pathname.c_str(), proj_id);int shmid = shmget(key, size, IPC_CREAT|0666);char* st = (char*)shmat(shmid, nullptr, 0);  //得到共享内存在虚拟内存空间中的起始地址int fd = open(pipepath.c_str(), O_WRONLY);string input = "";while (true) {cout << "[client write]$ ";getline(cin, input);write(fd, "c", 1);memcpy(st, input.c_str(), input.size());   //写数据}shmdt(st);
}

4 消息队列

  • 创建——msgget <-----ftok

image-20231121193308813

  • 删除——msgctl

    image-20231121193220744

  • 发送/接收数据块——msgsndmsgrcv

image-20231204105114938

  • 查看消息队列
ipcs -q
  • 删除消息队列
ipcrm -q [msqid]

5 IPC在内核中的数据结构

系统中维护一个struct ipc_prem*数组,通过shmidmsqidsemid作为下标访问,因为每一个shmid_dsmsqid_dssemid_ds结构体的第一个字段均为ipc_prem,所以系统可以通过将ipc_prem*强制类型转换为对应消息队列、共享内存或者信号量的结构体,从而进行管理

image-20231121200212372

image-20231121200321590

image-20231121200538938

image-20231121200557501


  1. 图片中两个进程通过共享内存建立通信,此时共享内存的连接数为2 ↩︎

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

案例二:SQL Server数据库的备份和还原

1、备份类型。 在 SQL Server 中提供了三种常用的备份类型&#xff0c;分别是完整备份&#xff0e;差异备份和事务日志备份。 完整备份&#xff1a; 完整备份包括对整个数据库、部分事务日志、数据库结构和文件结构的备份。完整备份代表的是备份完成时刻的数据库。 完整备份是…

【Hydro】Python绘制降雨径流双Y轴成果图

目录 说明源代码说明 双y轴图像具有单y轴图像没有的对比效果,通常会用来绘制降雨径流成果图,在MATLAB中有plotyy函数可以实现,Python的实现方式没有MATLAB那样方便,不过实现效果却也不见得差。 Python中的matplotlib通常使用twinx来生成双Y轴,下图便是使用matplotlib绘制…

8、操作符重载

友元 可以通过friend关键字&#xff0c;把一个全局函数、另一个类的成员函数或者另一个类整体&#xff0c;声明为授权类的友元友元拥有访问授权类任何非公有成员的特权友元声明可以出现在授权类的公有、私有或者保护等任何区域且不受访问控制限定符的约束友元不是成员&#xf…

elment-table设置el-table-column的label里面的文字换行居中显示

效果图如下&#xff1a; 直接上代码&#xff1a; <el-table class"ut-mt-2" row-key"company" default-expand-all:data"stateQuery.data" style"width: 100%":tree-props"{ children: departList, hasChildren: hasChildre…

Si24R03—低功耗 SOC 芯片(集成RISC-V内核+2.4GHz无线收发器)

Si24R03是一款高度集成的低功耗SOC芯片&#xff0c;其集成了基于RISC-V核的低功耗MCU和工作在2.4GHz ISM频段的无线收发器模块。 MCU模块具有低功耗、Low Pin Count、宽电压工作范围&#xff0c;集成了13/14/15/16位精度的ADC、LVD、UART、SPI、I2C、TIMER、WUP、IWDG、RTC等丰…

PandoraFMS 监控软件 任意文件上传漏洞复现

0x01 产品简介 Pandora FMS 是用于监控计算机网络的软件。 Pandora FMS 允许以可视化方式监控来自不同操作系统、服务器、应用程序和硬件系统(例如防火墙、代理、数据库、Web 服务器或路由器)的多个参数的状态和性能。 0x02 漏洞概述 PandoraFMS upload_head_image.php 接…

NLP项目实战01之电影评论分类

介绍&#xff1a; 欢迎来到本篇文章&#xff01;在这里&#xff0c;我们将探讨一个常见而重要的自然语言处理任务——文本分类。具体而言&#xff0c;我们将关注情感分析任务&#xff0c;即通过分析电影评论的情感来判断评论是正面的、负面的。 展示&#xff1a; 训练展示如下…

【基于LicheePi-4A的 人脸识别系统软件设计】

参考:https://www.xrvm.cn/community/post/detail?spm=a2cl5.27438731.0.0.31d40dck0dckmg&id=4253195599836418048 1.前言 原先计划做基于深度学习的炸药抓取和智能填装方法研究,但是后来发现板卡不支持pyrealsense2等多个依赖包。因此改变策略,做一款基于LicheePi…

【Python】列表乘积的计算时间

概述 使用以下三种模式测量了计算列表乘积所需的时间。 使用 for 语句传递list使用math模块使用numpy 下面是实际运行的代码。 import timestart time.time() A [1] * 100000000 ans 1 for a in A:ans * a print("list loop:", time.time() - start)import m…

有哪些已经上线的vue商城项目?

前言 下面是一些商城的项目&#xff0c;需要练手的同学可以挑选一些来练&#xff0c;废话少说&#xff0c;让我们直接开始正题~~ 1、newbee-mall-vue3-app 是一个基于 Vue 3 和 TypeScript 的电商前端项目&#xff0c;它是 newbee-mall 项目的升级版。该项目包含了商品列表、…

内网环境下 - 安装linux命令、搭建docker以及安装镜像

一 内网环境安装docker 先在外网环境下载好docker二进制文件docker二进制文件下载&#xff0c;要下载对应硬件平台的文件&#xff0c;否则不兼容 如下载linux平台下的文件&#xff0c;直接访问这里即可linux版本docker二进制文件 这里下载docker-24.0.5.tgz 将下载好的文件…

计算机存储单位 + 程序编译过程

C语言的编译过程 计算机存储单位 头文件包含的两种方式 使用 C/C 程序常用的IDE 常用的C语言编译器&#xff1a; 在选择编译器时&#xff0c;需考虑平台兼容性、性能优化、调试工具和开发人员的个人偏好等因素。 详细教程可转 爱编程的大丙

Java编程中通用的正则表达式(一)

正则表达式&#xff08;Regular Expression&#xff0c;简称RegEx&#xff09;&#xff0c;又称常规表示法、正则表示、正规表示式、规则表达式、常式、表达式等&#xff0c;是计算机科学中的一个概念。正则表达式是用于描述某种特定模式的字符序列&#xff0c;特别是用来匹配、…

持续集成和持续交付

引言 CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一种面向开发和运维团队的解决方案&#xff0c;CI/CD 主要针对在集成新代码时所引发的问题&#xff08;亦称&#xff1a;“集成地狱”&#…

idea__SpringBoot微服务05——JSR303校验(新注解)(新的依赖),配置文件优先级,多环境切换

JSR303校验&#xff0c;配置文件优先级&#xff0c;多环境切换 一、JSR303数据校验二、配置文件优先级三、多环境切换一、properties多环境切换二、yaml多环境切换————————创作不易&#xff0c;如觉不错&#xff0c;随手点赞&#xff0c;关注&#xff0c;收藏(*&#x…

电脑待机怎么设置?让你的电脑更加节能

在日常使用电脑的过程中&#xff0c;合理设置待机模式是一项省电且环保的好习惯。然而&#xff0c;许多用户对于如何设置电脑待机感到困扰。那么电脑待机怎么设置呢&#xff1f;本文将深入探讨三种常用的电脑待机设置方法&#xff0c;通过详细的步骤&#xff0c;帮助用户更好地…

【数据结构】顺序表的定义和运算

目录 1.初始化 2.插入 3.删除 4.查找 5.修改 6.长度 7.遍历 8.完整代码 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &…

web前端开发html/css练习

目标图&#xff1a; 素材&#xff1a; 代码&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"…

使用RSA工具进行对信息加解密

我们在开发中需要对用户敏感数据进行加解密&#xff0c;比如密码 这边科普一下RSA算法 RSA是非对称加密算法&#xff0c;与对称加密算法不同;在对称加密中&#xff0c;相同的密钥用于加密和解密数据,因此密钥的安全性至关重要;而在RSA非对称加密中&#xff0c;有两个密钥&…

【USRP】5G / 6G OAI 系统 5g / 6G OAI system

面向5G/6G科研应用 USRP专门用于5G/6G产品的原型开发与验证。该系统可以在实验室搭建一个真实的5G 网络&#xff0c;基于开源的代码&#xff0c;专为科研用户设计。 软件无线电架构&#xff0c;构建真实5G移动通信系统 X410 采用了目前流行的异构式系统&#xff0c;融合了FP…