【操作系统笔记十一】进程间通信
Linux文件系统
inode 节点 (index node):给每个文件赋予一个称为 i 节点的数据结构。
inode 一开始是存储在硬盘中的,只有当文件被打开的时候,其对应的 i 节点才加载到内存中。
总结:
-
Linux 中,用户态通过读写文件的 Api 进行系统调用,在内核态中,上层是虚拟文件操作系统 VFS,它为用户态提供统一接口,屏蔽底层实现细节,VFS 层定义了底层具体的文件系统需要实现的接口,VFS 层往下对接不同的具体的文件系统如 ext4,具体的文件系统再去操作磁盘的文件块信息
-
Linux 中每个文件对应一个称为
iNode
的数据结构,inode
中包含了文件的元数据以及若干的块地址信息,inode
一开始存储在磁盘中,当文件被打开时,inode
节点会被加载到内存当中 -
每个进程的
task_struct
中包含files_struct
结构体,files_struct
中又包含一个fd
数组fd_array
,fd_array
中则包含对应文件的文件操作符file
,file
文件操作符是通过inode
去读写文件的,inode
中定义了inode_options
,而具体的底层文件系统则实现了inode_options
中定义的对应读写接口的具体方法
管道
Linux 进程间通信方式:管道、共享内存、信号量、消息队列
① 匿名管道
② 命名管道
管道的实现
一个文件可以同时被多个进程访问
所以,我们可以使用文件来实现进程间的通信,管道就是基于文件系统来实现的。
实现进程和其子进程之间的管道通信
父进程在复制子进程时,会把父进程的相关信息全部拷贝过来,其中就包括file_struct
结构体,而这个结构体中就包含了文件读写inode
的两个文件描述符,一个 file_0
只读, 一个 file_1
只写,由于是复制的,所以父子进程的这俩文件描述符是指向的同一个文件的inode
。
此时把父进程的 file_0
只读文件描述符 close 掉,把子进程的 file_1
只写文件描述符 close 掉,父进程只保留只写文件描述符,子进程只保留只读文件描述符,这样父进程就可以和子进程通信了(父进程只写,子进程只读,半双工)。
匿名管道的实现
匿名管道底层实现:
匿名管道通过虚拟文件系统 VFS 调用底层的 pipefs 内存文件系统,也就是说底层实现是基于 pipefs 文件系统的。
pipefs 文件系统的数据结构:https://www.processon.com/view/link/62822757e401fd36f6bcc5dd
管道在内存中的实现本质就是一段内核 buffer 内存,不同的文件操作符(一个读一个写)对这段 buffer 进行读写操作。
关于 ps -ef | grep systemd 命令背后的匿名管道的底层实现数据结构:
命名管道底层实现流程图:
总结
-
管道是基于文件系统来实现的,也就是多个进程对同一个文件进行读写来实现进程间通信
-
进程和子进程之间的管道通信:父进程在
fork
子进程时,会把父进程相关信息全部拷贝过来,其中包括file_struct
结构体,file_struct
中包含了文件读写inode
的两个文件描述符,一个file_0
只读,一个file_1
只写,由于是复制的,所以父子文件的这俩文件描述符是指向同一个文件的inode
, 此时把父进程的file_0
只读 fd 关闭掉,然后再把子进程的file_1
只写 fd 关闭掉,父进程只保留只写 fd ,子进程只保留只读 fd ,这样父进程就可以和子进程进行通信了(父进程写,子进程读)。 -
匿名管道的虚拟文件系统 VFS 对应的底层文件系统实现是基于
pipefs
内存文件系统 -
管道在内存中的实现本质就是一段内核 buffer 内存,不同的文件操作符(一个读一个写)对这段 buffer 进行读写操作。
-
用户态:
read
/write
→ 内核态 VFS:task_struct
→files_struct
→fd_array
→fds[0]
fds[1]
→file0
file1
→file_opts
→inode
→pipe_inode_info
→pipe_bufs
共享内存 (shared memory)
创建共享内存
shmget
- allocates a System V shared memory segment
#include <sys/ipc.h>
#include <sys/shm.h>
// 返回根据 key 生成的 shmid
int shmget(key_t key, size_t size, int shmflg);
参数含义:
-
key
:唯一标识新创建的共享内存 -
size
:共享内存的大小,向上取整成PAGE_SIZE
的倍数 -
shmflg
:一些标志信息IPC_CREAT
:根据key
判断对应的共享内存段是否存在,如果不存在,则创建;如果存在,则返回已经存在的共享内存段IPC_EXCL
: 和IPC_CREAT
一起用,如果已经存在key
对应的共享内存 则失败读写权限信息
映射共享内存
shmat
— 映射共享内存到进程的虚拟地址空间,返回映射的虚拟内存段的起始地址shmdt
— 解除映射,如果成功返回 0,否则返回 -1
#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
参数含义:
shmid
:共享内存的唯一标识id
,即填入由shmget
函数返回的值shaddr
: 内存映射起始地址,如果是NULL
的话,内核会分配shaflg
:是一组标志位,通常为0
。
注意:创建和映射共享内存操作只是在内核中维护一些数据结构,并没有真的分配物理内存。真正分配物理内存是在访问这块虚拟内存地址中的数据发生缺页异常时,由缺页异常处理程序维护进程页表中的虚拟页号和物理页号的映射关系的。
这里进程 A 和进程 B 访问的是同一块物理内存上的相同的物理页。
参考代码:
共享内存的底层原理是基于 tmpfs 文件系统:https://www.processon.com/view/link/6277c3921e085327716f5971
总结
-
共享内存的原理:不同进程的虚拟内存地址会映射到相同的物理内存上,这样两个进程通过访问同一块物理内存,达到通信的目的。(一般情况下,不同进程的虚拟地址是映射到不同物理地址的)
-
在创建共享内存时并没有真的分配物理内存,真的分配是进程在读、写数据的时候,发生缺页异常,由缺页异常处理程序分配共享内存(物理内存)的页号到进程的虚拟页表中
-
共享内存的底层原理是基于
tmpfs
文件系统, Linux中一切皆文件
问题:mmap 内存映射和 shm 共享内存有什么区别?
- Linux 中的内存映射是指将一块虚拟地址内存空间和一个文件对象关联起来,以初始化这块虚拟内存的内容,文件对象可以是一个普通磁盘文件,也可以是一个匿名文件(一块只包含二进制零的物理内存)
- mmap 内存映射时,被映射的对象可以是一个磁盘文件,也可以是一个请求二进制零的匿名对象。如果是前者,在发生缺页异常时,缺页异常处理程序除了需要维护页表外,还需要将磁盘文件内容加载到物理内存中;如果是后者,则就相当于将一块物理内存和虚拟内存进行映射。
- shm 共享内存映射是直接每个进程将虚拟内存映射到同一块物理内存,不涉及到磁盘文件。shm 保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。
- 所以可以简单的认为 mmap 主要是用于映射磁盘文件的,而 shm 是直接用于映射物理内存的
- mmap 有一个好处是,把文件保存在磁盘上,当设备机器重启时,这个文件还保存了操作系统同步的映像,所以 mmap 不会丢失,但是 shm 就会丢失。
信号量
在一个进程内,多个线程同时更新共享资源,有数据并发安全问题,解决方案有:
- ① 原子操作
- ② 锁机制 - 管程
- ③ 信号量
多个进程同时更新共享内存(共享资源),也有数据安全问题,解决方案:信号量
IPC 的信号量 (semaphore)
原理思想和并发编程中的信号量是一样的,但是两者的实现完全不同:
-
IPC 的信号量实现很复杂,是在内核态中实现的,而并发编程中的信号量是在用户态实现,基于原子操作实现
-
IPC 的信号量是操作系统层面用于解决多个进程之间的共享内存并发读写问题,并发编程中的信号量用于解决同一个进程的多个线程之间的共享资源读写问题
一个是在内核态实现的,一个是应用程序代码中实现的。
参考代码:
消息队列
创建消息队列:msgget
- get a System V message queue identifier
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg); // 函数返回返回新创建的消息队列的 id
参数含义:
-
key
:唯一标识新创建的消息队列 -
msgflg
:一些标志信息IPC_CREAT
:根据key
判断对应的共享内存段是否存在,如果不存在,则创建;如果存在,则返回已经存在的共享内存段IPC_EXCL
:和IPC_CREAT
一起用,如果已经存在key
对应的共享内存,则失败读写权限信息
发送和接收消息:msgsnd
, msgrcv
- System V message queue operations
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // 返回值:成功返回0;失败返回-1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtype, int msgflg); // 成功返回接收个数,失败,返回-1
msqid
: 由msgget
函数返回的消息队列的标识符msgp
: 消息缓冲区指针,指针指向准备发送/接收的消息msgflg
: 为0
表示阻塞方式,设置IPC_NOWAIT
表示非阻塞方式异步接发消息msgsz
: 是msgp
指向的消息长度,这个长度不含保存消息类型的那个long int
长整型msgtype
:
等于0
,那么读取消息队列中的第一条消息
大于0
,那么读取消息队列中的第msgtype
条消息(这里是读取类型等于msgtype
的第一条消)
小于0
,那么读取小于等于msgtype
绝对值最小的msgtype
的消息
参考代码:
int main() {int mq_id = get_mq_id(); struct msg_buffer buffer;printf("enter message type: "); scanf("%d", &buffer.mtype);printf("enter message contenit:");scanf("%s", &buffer.mtext);int len = strlen(buffer.mtext) + 1;if (msgsnd(mq_id, &buffer, len, IPC_NOWAIT) == -1) { perror("fail to send message.");exit(1); }return 0;
}
#include <string.h>
#include "mq.h" int main() {int mq_id = get_mq_id(); struct msg_buffer buffer; int type;scanf("%d", &type);if (msgrcv(mq_id, &buffer, 1024, type, IPC_NOWAIT) == -1) { perror("fail to recv message.");exit(1); }printf("received message type : %d, text: %s, \n", buffer.mtype, buffer.mtext); return 0;
}
相关文章:

【操作系统笔记十一】进程间通信
Linux文件系统 inode 节点 (index node):给每个文件赋予一个称为 i 节点的数据结构。 inode 一开始是存储在硬盘中的,只有当文件被打开的时候,其对应的 i 节点才加载到内存中。 总结: Linux 中,…...
【操作系统】聊聊Linux软中断
什么是中断 中断是系统用来响应硬件设备请求的一种机制,会打断进程的正常调度和执行,转而去执行内核中的中断处理程序。 比如你正在看书,你女朋友叫你出去逛街。你就需要先放下手里的事情,然后逛街。回来之后,在接着看…...

公众号迁移个人可以迁移吗?
公众号账号迁移的作用是什么?只能变更主体吗?很多小伙伴想做公众号迁移,但是不知道公众号迁移有什么作用,今天跟大家具体讲解一下。首先公众号迁移最主要的就是修改公众号的主体了,比如我们公众号原来是A公司的&#x…...

全国职业技能大赛云计算--高职组赛题卷⑤(容器云)
全国职业技能大赛云计算--高职组赛题卷⑤(容器云) 第二场次题目:容器云平台部署与运维任务2 基于容器的web应用系统部署任务(15分)任务3 基于容器的持续集成部署任务(15分)任务4 Kubernetes容器…...

支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答
很多投资者都知道,Renko图表和普通日本烛台都会采用相同的交易信号,即支撑位和阻力位。那么支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答。 这些信号在任何时间框架上都会出现,且在蜡烛图交易中颇受欢迎。对于Renko图表而言…...

如何在32位MCU用printf()函数打印64位数据
1. 在32位MCU上定义64位变量: unsigned long long time_base; unsigned long long temp_time;2. 调用打印函数: printf("RFID:time_base:%d\r\n",time_base); printf("RFID:temp_time:%d\r\n",temp_time); printf("RFID:Ru…...

Python爬虫程序设置代理常见错误代码及解决方法
Python爬虫程序设置代理是爬虫程序中常用的技巧,可以有效地绕过IP限制,提高爬虫程序的稳定性和效率。然而,在设置代理时,常会出现各种错误代码,这些错误代码可能会影响程序的正常运行,甚至导致程序崩溃。本…...

3D点云目标检测:Centerformer训练waymo数据集
一、环境准备 项目地址:centerformer 1.0、基础环境 python 3.8.0 torch 1.9.1cu111 waymo-open-dataset-tf-2-6-0 1.4.9 spconv 1.2.1 其余按照requirement.txt里安装就行 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt由于我本人是在…...

火山引擎DataLeap推出两款大模型应用: 对话式检索与开发 打破代码语言屏障
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 自上世50年代,以“计算机”作为代表性象征的信息革命开始,社会对于先进生产力的认知便开始逐步更迭——从信息化(通常认为是把企…...

windows上配置vscode C/C++代码跳转
windows上配置vscode C/C代码跳转 安装插件 C/C 官方的 C/C 插件,必备的插件,是代码跳转、自动补全、代码大纲显示等功能的基础。 Gtags C/C GNU Global GNU Global除了安装该插件之外,还需要在本地下载安装GNU Global工具。多看下插件…...

【Xilinx】基于MPSoC的OpenAMP实现(一)
【Xilinx】基于MPSoC的OpenAMP实现(一) 一、开发环境1、开发思路2、下载官方bsp包 二、编译Linux1、配置petalinux环境变量2、创建工程3、进入目录4、设置缓存目录(重点:可离线编译,加快编译速度)5、配置u-…...

代码随想录算法训练营总结篇|完结撒花
完结撒花,真不敢相信60天坚持下来了。 算法一直是我的超级超级弱项,属于小白中的小白。一开始是想自己刷的,打开leetcode第一题,吼哟好家伙,梦开始的地方直接破碎。之前刷B站的时候就有学习up推荐算法可以看看代码随想…...

uniapp、vue实现滑动拼图验证码
uniapp、vue实现滑动拼图验证码 实际开发工作中,在登陆的时候需要短信验证码,但容易引起爬虫行为,需要用到反爬虫验证码,今天介绍一下拼图验证码,解决验证码反爬虫中的滑动验证码反爬虫。滑动拼图验证码是在滑块验证码…...

【ArcGIS】土地利用变化分析详解(矢量篇)
土地利用变化分析详解-矢量篇 土地利用类型分类1 统计不同土地利用类型的面积/占比1.1 操作步骤Step1:Step2:计算面积Step3:计算占比 2 统计不同区域各类土地利用类型的面积2.1 操作步骤 3 土地利用变化转移矩阵3.1 研究思路3.2 操作步骤 4 分…...

VS2022创建控制台应用程序后没有Main了,如何显示Main?
文章目录 问题描述原因解决方案简单的顶级语句试用计算器 其他文章 问题描述 用VS2022创建一个控制台应用后,没有名称空间和Main函数了,只有一个WriteLine,如下所示。 // See https://aka.ms/new-console-template for more information Co…...

当当网商品详情数据接口
当当网商品详情数据接口可以通过当当网的开放平台获取相关信息。您可以注册当当开放平台账号,并按照要求提交申请获取API接口的调用凭证。获得授权后,您将会收到一组AccessKey和SecretKey。使用编程语言(如Java)调用API接口&#…...

ultraEdit正则匹配多行(xml用)
在ultraEdit中,我想选取<channel到</channel>之间的多行(进行删除)。在perl模式下,命令为“<channel[\s\S]?</channel>”。下面是xml文件: <!--This XML file does not appear to have any sty…...
Mac上的utools无法找到本地搜索插件
utools安装地址 utools本地搜索用法 目前本地搜索只在win下,mac无福了 Mac可用cmdspace方法使用聚焦搜索,来搜索本地文件...

docker部署nginx下日志自动切割方法
前言:nginx采用docker部署,简单方便,但出现一个问题,就是日志没有自动切割,导致access.log 无限增大。如果非docker安装,则nginx的日志默认有切割的,那docker为何没有呢,最后发现&am…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...