Linux进程间通信——探索共享内存—— 剖析原理, 学习接口应用

        前言:本节内容主要讲解进程间通信的, systemV版本下的共享内存。 共享内存,顾名思义, 其实就是一块内存, 它不同于管道是一个文件。 所以它的传输速度是很快的。 因为管道是文件,有缓冲区, 而共享内存只是一块内存, 可以直接与进程挂接, 直接拷贝。

        ps:本节内容适合了解一些管道知识的友友们进行观看哦

目录

进程间通信的本质

共享内存的原理

共享内存相关接口

shmget——创建共享内存

shmflg

key

shmat——进程挂接

 ​编辑

shmdt——进程去关联

 指令

ipcs -m

应用

准备文件

准备makefile

Com.hpp

GetKey——封装获取key值的函数

CreatShareMemory——获取共享内存

CreatShm和GetShm——获取共享内存

processa.cpp

processb.cpp

运行程序

key和shimd的区别

共享内存的生命周期


进程间通信的本质

        进程间通信的本质就是——先让不同的进程, 看到同一份资源。(这是为了给通信奠定前提条件)

共享内存的原理

        上面的这一张图, 其实就是叫做共享内存。 因为共享内存的本质, 就是两个进程能够看到同一份资源, 而上面, 就做到了两个进程看到同一份资源。  其中左边是一个进程的PCB, 然后右边是一个进程的PCB。 同时这两个PCB都有它们对应的虚拟地址空间然后物理内存中有一块共享资源, 两个进程都可以通过页表的映射看到这份共享资源。—— 这就是共享内存。 

        对于上面这一张图来说, 我们的第一步就是申请内存。 第二步然后就是将申请好的内存分别挂接到两个进程的进程地址空间, 然后返回我们的地址空间的地址即可。但是这是申请共享内存的流程。 如果我们要释放共享内存呢?

        我们申请是由操作系统在内存中开辟一段地址空间, 然后把虚拟地址到物理地址之间的映射关系在页表中一填, 最后再挂接。 那么共享内存就创建好了。 ——而释放空间内存其实就是反过来, 一定是如何创建的, 那么就如何释放的。 ——所以第一步就是进程和共享内存去关联, 就是将页表之中虚拟地址和物理地址的映射关系去掉 然后就释放共享内存。 

        现在, 有一个问题就是——上面的操作是由进程直接做的吗? 答案是不是的, 进程没有资格直接访问共享内存的, 必须由操作系统来做。 

        那么我们就能看到一个典型的现象。 ——我们的进程需要通信, 然后操作系统就帮进程建立信道。 ——这里进程就是需求方, 而操作系统就是执行方。 进程解决为什么我们要建立共享内存操作系统解决的是如何建立共享内存。 那么就意味着我们的操作系统是必须要给进程提供系统调用!!!

        那么,我们的操作系统当中有着各种通信, 所以一定有着非常多的共享内存, 所以操作系统要不要将共享内存管理起来呢?——答案是需要的, 而管理的方式就是先描述再组织。 也就是说, 一定会有内核数据结构来描述共享内存

共享内存相关接口

shmget——创建共享内存

        上面的shmget函数, 就是创建共享内存的接口。 使用它需要包含sys/ipc.h、sys/shm.h头文件。 同时三个参数, 一个返回值int类型。 ——其中, size和返回值比较好说, size就是我们要创建的共享内存的大小, 返回值int就是共享内存标识符。 key和shmflg是没有办法一下子说清的, 下面我们来谈一谈这两个参数的相关问题:

shmflg

        shmflag是创建的共享内存的模式, 这里说两种常用的使用选项:

        IPC_CREAT(单独使用)如果你申请的共享内存, 不存在, 就创建, 存在就获取并返回。

        IPC_CREAT | IPC_EXCL如果你申请的共享内存不存在, 就创建, 存在就出错返回。 ——这个选项就可以确保如果我们申请内存成功了, 这个内存一定是新的!!!(IPC_EXCL不单独使用!

key

        在谈key之前我们先思考一个问题, 就是对于一块共享内存, 我们如何知道他存不存在呢? 或者说我们怎么保证让不同的进程看到同一个共享内存呢?——带着这两个问题, 我们来谈key:

        1、key是一个数字, 这个数字是几, 不重要, 关键是它必须在内核种具有唯一性, 能够让不同进程进行唯一性标识。

        2、第一个进程可以根据key创建共享内存, 第二个之后的进程, 他们只要拿着同一个key, 就可以和第一个进程看到同一个共享内存了!!!

        3、对于一个创建好的共享内存, key在哪?——这个可以直接挑明, 其实就在共享内存的描述对象中!!!

        4、命名管道是通过路径 + 文件名来确定唯一的管道的!!!而我们的key, key类似于路径, 同样具有唯一性。共享内存就是通过key来确定唯一性!!!

         现在有一个问题, 第一次创建共享内存的时候的key, 是如何有的呢?

        这个函数, 就是用来在第一次创建共享内存时, 用来生成key的函数。 所以,key不是直接定义的!!!

        上面的图中, key是如何创建出来的呢? 我们通过上面的蓝色框框其实可以看到, 这个函数是通过转化路径名和一个项目标识符来生成一个key

        那么问题来了, 我们的key能不能随便定义一个出来呢?——理论上是可以的, 因为我们两个进程看到同一个资源, 本质上就是需要拿着同一把钥匙, 而key就是这一把钥匙。 ——所以我们不需要把ftok想的太复杂, 这个函数当生成key的时候不需要去系统中查找哪个key使用过, 哪个没有使用过。 而是类似于哈希函数, 使用了一套算法, pathname和proj_id进行数值计算即可!!

        那么, 为什么不能让操作系统自己去生成呢?为什么要让用户自己去设置呢?——虽然操作系统做这些工作很简单, 但是如果操作系统给我们生成了这么一个key, 我们进行通信的时候, 我们怎么把这个key交给另一个进程呢? ——这里可不可以直接将返回的key交给另一个进程呢这个不可以的, 因为我们现在要解决的就是进程间通信的问题, 而想要将key交给另一个进程, 需要进程通信, 这就陷入了一个死循环。 所以, 我们的key, 就势必不能由操作系统自己决定。 ——这个也就说明, 我们的ftok, 与其说是用户自己指定的key, 不如说是用户之间约定的key!!!只要有两个用户, 他们同时使用进程, 同时使用进程, 使用同一个路径, 同一个id, 那么他们就能够进行通信!!!

shmat——进程挂接

 

        什么是进程挂接?我们要访问一个共享内存, 虽然共享内存是操作系统的, 虽然我们使用shmid只是获得了共享内存的编号。 但是既然是内存, 那么如果我们操作系统提供一个系统接口, 通过shmid找到目标的共享内存, 然后让这个共享内存和进程的地址空间通过页表建立了联系。 那么我们的进程, 是不是就相当于能够访问这块共享内存了? 而这个过程, 就叫做挂接!!!

        而一个共享内存可以有多个进程和它挂接起来, 这个数量, 就是nattch, 我们如何观察到这个nattch, 可以使用ipcs -m指令。ps:讲解在指令部分

shmdt——进程去关联

        有进程挂接, 那么就有进程去关联, 也就是下面的shmdt, 传送的参数是shmat返回的地址空间的地址。 

 

        这里我们思考的是我们只有这个共享内存的起始地址, 这个函数是如何知道我们要取消关联的共享内存有多大呢? ——这个问题和realloc, free等函数类似, 他们都是只需要传送首地址, 不需要传送大小或者末尾地址, 这就说明了一定有我们在用户层看不到的东西, 在管理着我们的内存!!!

        并且, 我们的去关联的流程就是——根据页表找到我们的物理内存, 把页表清掉, 根据inode属性, 让物理内存减减, 如果要释放, 就按照属性里面的大小进行释放即可!

 指令

ipcs -m

        ipcs -m可以查看一个共享内存的各个属性:

  • shimd:共享内存的编号。
  • perms:共享内存的权限。
  • bytes:共享内存的大小, 这个大小是以4096唯一个单位的, 也就是说, 虽然我们上面显示我们创建了4097个大小的共享内存, 但其实操作系统给我们开辟的是8k多字节。 从c语言的角度, 这部分多申请的空间叫做cookie。 ——实际上, 我们c语言在申请堆空间的时候,要不要管理所谓的堆空间呢?——答案是肯定要的, 所以就要先描述再组织, 所以我们c语言多申请的那部分空间, 也有对应的属性!!!
  • nattch:代表了该共享内存的挂接数量。

应用

准备文件

ps:Log.hpp我们在之前的文章已经实现过了, 所以这里直接拿来用了, 不会再实现一遍, 不会的友友可以去前面的文章看一看:linux进程间通信——学习与应用命名管道, 日志程序的使用与实现-CSDN博客

准备makefile

有两个cpp文件需要编译。所以需要.PHONY, 然后编译两个文件。 最后clean, 删除两个文件。

.PHONY:all
all:processa.exe processb.exe processa.exe:processa.cppg++ -o $@ $^ -std=c++11
processb.exe:processb.cpp g++ -o $@ $^ -std=c++11.PHONY:clean
clean: rm -f processa.exe processb.exe

Com.hpp

GetKey——封装获取key值的函数

我们知道ftok函数的第一个参数是路径, 第二个参数是项目id。 我们就可以先定义两个常量——pathname和proj_id;然后, 我们又可以创建一个log函数, 让log函数来打印日志; ftok需要包含头文件sys/ipc.h以及sys/types.h。 同时其他的string, cstring, cerrno, 等等都包含进来需要用到。——然后其中的pathname其实就是传给ftok的路径函数, 其中的proj_id就是项目id。 通过这两个我们就可以获得一个key值。 用来创建共享内存。 

#pragma once#include"Log.hpp"
#include<cerrno>
#include<cstring>
#include<string>
#include<iostream>#include<sys/ipc.h>
#include<sys/types.h>using namespace std;const string pathname = "/home/_mian_yang";
const int proj_id = 0x88881; Log log;//获取key
key_t GetKey()
{key_t k = ftok(pathname.c_str(), proj_id);   //ftok拿到k函数if (k == -1)  //如果k == -1, 那么就拿到k失败。{log(Fatal, "ftok error: %s", strerror(errno));  //打印错误信息exit(1);}log(Info, "ftok success! Key is: #d", k);  //创建k成功, 返回kreturn k; }

CreatShareMemory——获取共享内存

        这是第二层封装, 封装了GetKey函数。 用来获取共享内存。 外层函数传递flag, 也就是贡献内存的打开方式——这个打开方式就是用来区分共享内存共享内存是已经创建好的还是第一次创建的。在本次实验中, 我们prcessa进程用来第一次创建共享内存, 而processb用来获取已经创建好的共享内存。 那么这两个进程内部调用CreatShareMemory的时候就要传递不同的flag: processa进程传递IPC_CREAT | IPC_EXCL | 权限, processb传递IPC_CREAT 

另外, 因为shmget创建共享内存的函数需要传递一个size用来规定共享内存的大小, 所以, 我们可以在上面创建一个size常量。 另外使用shmget也需要包含一下sys/shm.h头文件

//
#pragma once#include"Log.hpp"
#include<cerrno>
#include<cstring>
#include<iostream>
#include<sys/ipc.h>
#include<sys/types.h>
#include<string>
#include<sys/shm.h>
using namespace std;const int size = 4096;   //4kb
const string pathname = "/home/_mian_yang";
const int proj_id = 0x88881; 
Log log;//获取key
key_t GetKey()
{key_t k = ftok(pathname.c_str(), proj_id);   //ftok拿到k函数if (k == -1)  //如果k == -1, 那么就拿到k失败。{log(Fatal, "ftok error: %s", strerror(errno));  //打印错误信息exit(1);}log(Info, "ftok success! Key is: #d", k);  //创建k成功, 返回kreturn k; }int CreatShareMemory(int flag)
{key_t key = GetKey(); //获取keyint shmid = shmget(key, size, flag);if (shmid == -1){log(Fatal, "creat share memory error: %s", strerror(errno)); //创建共享内存失败, 打印错误消息exit(1);}log(Info, "creat share memory is success! shmget is: %d", shmid); //创建共享内存成功, 打印共享内存的的shmidreturn shmid;
}

CreatShm和GetShm——获取共享内存


int GreatShm()
{return GreatShareMemory(IPC_CREAT | IPC_EXCL | 0666); //第一次创建需要判断是否存在}int GetShm()
{return GreatShareMemory(IPC_CREAT); //获取的时候不需要判断是否存在
}

processa.cpp

        我们在本次实验中要实现a来打印, b来输入的实验效果。所以我们的a要从内存中读到数据。 在b中把数据写入内存。 

        另外, 我们设计让a来创建共享内存, 创建好共享内存后, 要知道, 我们平时向内存中读数据, 必须要获取这个内存的地址,但是我们现在只有共享内存的编号shmid,  那么如何获取这个地址? ——这就用到了挂接, 我们将共享内存挂接到当前进程的虚拟地址上, 那么进程访问虚拟地址就是在访问共享内存!!!(注:这里面使用了shmctl, 这个函数的使用难度很大, 这里博主使用了IPC_STAT选项, 目的是为了获取shmid对应共享内存中的数据, 那么获取到那里呢, 就需要提前创建一个描述共享内存的结构体, 博主定义的是shmds, 所以就有了下面的代码)

#include"Com.hpp"
#include"Log.hpp"extern Log log; //拿到Com.hpp里面创建的哪个全局的静态对象, int main()
{//先创建共享内存int shmid = GreatShm();//函数内部自动判断, 无需手动判断//挂接共享内存到到vm_area_structchar* shmaddr = (char*)shmat(shmid, nullptr, 0); //这个返回值, 就是//共享内存的虚拟地址, 我们利用虚拟地址, 就可以访问共享内存struct shmid_ds shmds; //创建一个结构体, 用来获取共享内存的数据while(true){cout << "client say@ " << shmaddr << endl;sleep(1);shmctl(shmid, IPC_STAT, &shmds); //读取共享内存的各个属性//将各个数据打出来!cout << "shm size: " << shmds.shm_segsz << endl;cout << "shm nattch: " << shmds.shm_nattch << endl;cout << "shm key: " << shmds.shm_perm.__key << endl;}    shmdt(shmaddr);shmctl(shmid, IPC_RMID, nullptr); //删除return 0;
}

processb.cpp

b进程我们用来读取数据, 同样需要挂接。 不同的是我们要创建一个缓冲区buffer, 来接收我们要打印的数据,然后向内存中将缓冲区的数据写进去。 

#include"Com.hpp"
#include"Log.hpp"int main()
{int shmid = GetShm();//获取共享内存//向里面写东西, 写东西就需要获得这个共享内存的地址, 那么就需要挂接char* shmaddr = (char*)shmat(shmid, nullptr, 0); //挂接地址随机, 模式为0while (true){//先创建一个缓冲区string str;cin >> str;  //向缓冲区种写入数据snprintf(shmaddr, str.size(), "%s", str.c_str()); //将缓冲区的数据写入//共享内存}return 0;
}

运行程序

如下图是我们甘冈打开两个进程:可以看到, 此时的挂接个数是2.

然后我们在b进程输入一个你好啊, 观察一下——

我们就会发现数据已经打印进去了。 然后我们如果退出进程b, 挂接数会减到1:

——以上就是共享内存接口的相关应用。 

经过上面的实验, 我们可以很明显的看出来, 共享内存是没有同步机制的!!!!——而想要让共享内存拥有同步机制的效果, 可以使用管道!!!
 

key和shimd的区别

        了解了应用后, 我们就可以深究一下内部的原理了。

        首先我们谈的就是请问key和shmid有什么区别呢?我们知道, key是在操作系统标定唯一性的, shmid是在我们的进程里面的,用来标识资源的唯一性的。也就是说,key是操作系统层面的, 只有在创建共享内存的时候会用到key, 其他时候用不到。 

共享内存的生命周期

        我们第一次运行程序processa.exe的时候, key_t会创建成功, 并且共享内存被创建出来。但是当我们第二次运行processa.exe的时候, 我们会看到下面这种情况:

        也就是说, 我们的共享内存并没有随进程的退出而退出。 这里我们可以使用ipcs -m查看当前系统中所有的共享资源

        我们输入上面ipcs -m的指令可以看到我们刚刚创建的共享内存, 也就是说, 即便我们进程推出了, 但是我们的ipc资源, 还是存在的。 这说明如果我们不主动把共享内存关掉, 操作系统也不会给我们关。——这就是共享内存的特性。 ——也就是说, 贡献内存的生命周期是随着内核的!!!(管道文件的生命周期是随着进程的!!!)用户不主动关闭, 共享内存会一直存在, 除非内核重启!!!

        释放的方式分为两种——一种是使用指令进行释放, 另一种就是上面程序中使用的进程内函数调用shmctl进行控制释放共享内存。 

  •         指令释放:ipcrm -m 共享资源的shmid
  •         内部调用:shmctl(shmid, IPC_RMID, nullptr);

 

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!

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

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

相关文章

HDMI色块移动——FPGA学习笔记13

一、方块移动原理 二、实验任务 使用FPGA开发板上的HDMI接口在显示器上显示一个不停移动的方块&#xff0c;要求方块移动到边界处时能够改变移动方向。显示分辨率为800*480&#xff0c;刷新速率为90hz。&#xff08;480p分辨率为800*480&#xff0c;像素时钟频率Vga_clk 800x4…

Django后台管理复杂模型

【图书介绍】《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》_django 5企业级web应用开发实战(视频教学版)-CSDN博客 《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) Django框架…

libyuv之linux编译

文章目录 一、下载源码二、编译源码三、注意事项1、银河麒麟系统&#xff08;aarch64&#xff09;&#xff08;1&#xff09;解决 armv8-adotprodi8mm 指令集支持问题&#xff08;2&#xff09;解决 armv9-asve2 指令集支持问题 一、下载源码 到GitHub网站下载https://github.…

荣誉 | 分贝通入选2024「Cloud 100 China」

近日,2024 Cloud 100 China 榜单于美高梅酒店正式发布,这是靖亚资本和崔牛会联合推出的第三届榜单。 全球商旅管理、企业支出全流程管控、数据BI全方位降本、AI赋能高效出行体验.......近年来,分贝通不断精进产品能力及BI&AI能力,再次上榜。 本届评选,组委会基于过去一年融…

从HarmonyOS升级到HarmonyOS NEXT-环信SDK数据迁移

前言&#xff1a;2024年6月21日 HarmonyOS NEXT &#xff08;后续称之为 NEXT&#xff09; 正式发布&#xff0c;随着 NEXT 稳定版的逐渐临近&#xff0c;各个应用及SDK正在忙于适配 NEXT 系统&#xff0c;同样也面临着系统升级时如何对数据的迁移适配。本文通过使用环信 SDK 介…

Unity 高亮插件HighlightPlus介绍

仅对官方文档进行了翻译 注意:官方文档本身就落后实际,但对入门仍很有帮助,核心并没有较大改变,有的功能有差异,以实际为准.(目前我已校正了大部分差异,后续我会继续维护该文档) 为什么为该插件做翻译?功能强大,使用简单,且还在维护. 基于此版本的内置渲染管线文档 快速开始…

深度剖析去中心化存储:IPFS、Arweave 和 BNB Greenfield 的技术革新与生态系统演进

引言&#xff1a; 在数字时代的浪潮中&#xff0c;数据已然成为驱动创新和决策的核心资产。然而&#xff0c;随着数据量呈指数级增长&#xff0c;传统中心化存储模式面临着前所未有的挑战。安全漏洞、隐私泄露、数据垄断等问题日益凸显&#xff0c;促使技术界重新思考数据存储…

Html css样式总结

1.Html css样式总结 1.1. 定位position 布局是html中非常重要的一部分&#xff0c;而定位在页面布局中也是使用频率很高的方法&#xff0c;本章节为定位在布局中的使用技巧和注意事项。   position定位有4个属性&#xff0c;分别是static(默认&#xff09;&#xff0c;absol…

【C盘清理】Pycharm远程调试重度使用者C盘清理

文章目录 1 remote source 1 remote source 找到本地的这个路径C:\Users\verse\AppData\Local\JetBrains\PyCharm2022.3\remote_sources 这个文件夹是 PyCharm 在进行远程调试时使用的&#xff0c;它包含了远程服务器上的源代码副本。当你在 PyCharm 中设置远程调试并启动调试会…

[yotroy.cool] MGT 388 - Finance for Engineers - notes 笔记

个人博客https://www.yotroy.cool/,感谢关注~ 图片资源可能显示不全,请前往博客查看哦! ============================================================ Lecture 1 What is Accounting? The process of identifying, measuring and communicating economic informati…

docker容器中的内存占用高的问题分析

文章目录 问题描述原因分析分析1分析2验证猜想 结论和经验 问题描述 运维新增对某服务的监控后发现&#xff1a;内存不断上涨的现象。进一步确认&#xff0c;是因为有多个导出日志操作导致的内存上涨问题。 进一步的测试得出的结果是&#xff1a;容器刚启动是占用内存约为50M…

OceanBase 运维管理工具 OCP 4.x 升级:聚焦高可用、易用性及可观测性

可视化的管控平台&#xff0c;对 OceanBase 这类的分布式数据库及大规模数据的运维管理来说&#xff0c;是提升运维效率与数据库管理水平的重要工具。OceanBase 运维管理工具 OCP 作为专为OceanBase数据库设计的企业级全生命周期管理平台&#xff0c;为用户提供了全面的数据库可…

日志框架的使用

一、日志概述 日志&#xff1a;用来记录程序运行过程中的信息&#xff0c;并可以进行永久存储。 开发过程中可能会出现以下需求&#xff1a; 希望系统能记住某些数据是被谁操作的&#xff0c;比如被谁删除了&#xff1f;想分析用户浏览系统的具体情况&#xff0c;以便挖掘用…

我的AI工具箱Tauri版-FasterWhisper音频转文本

本教程基于自研的AI工具箱Tauri版进行FasterWhisper音频转文本服务。 进入软件后可以直接搜索 FasterWhisper 或者依次点击 Python音频技术/音频tools 进入该模块。 进入目录后需要进行一些基础配置&#xff0c;参数是默认的可以根据自己的机器进行一些简单的参数操作。 使用方…

【ViT+Dis】Deepfake Detection Scheme Based on Vision Transformer and Distillation

文章目录 Deepfake Detection Scheme Based on Vision Transformer and Distillationkey points深伪检测检测算法蒸馏法与教师网络实验训练:参数总结Deepfake Detection Scheme Based on Vision Transformer and Distillation 会议:2021 作者: key points 以往基于CNN结…

江科大笔记—软件安装

安装Keil5 MDK 资料下载&#xff1a;https://jiangxiekeji.com/download.html 密码&#xff1a;32 是否安装ULINK驱动&#xff0c;点击是 安装器件支持包 离线安装和在线安装 离线安装 在线安装 联网更新 软件的支持包目录已经更改&#xff0c;是否要重新加载支…

【算法】滑动窗口—最小覆盖子串

题目 ”最小覆盖子串“问题&#xff0c;难度为Hard&#xff0c;题目如下&#xff1a; 给你两个字符串 S 和 T&#xff0c;请你在 S 中找到包含 T 中全部字母的最短子串。如果 S 中没有这样一个子串&#xff0c;则算法返回空串&#xff0c;如果存在这样一个子串&#xff0c;则可…

单细胞BCR的分析Dandelion重注释的安装以及用法----11111

今天来学习下这个新的方法&#xff0c;主要是针对单细胞BCR 首先安装singularity Singularity 是一种容器化技术&#xff0c;类似于 Docker&#xff0c;专为高性能计算&#xff08;HPC&#xff09;和科学研究领域的需求设计。它允许用户在不同环境中运行和移植应用程序&#x…

免费PDF工具集套装,支持批量

软件介绍 PDFgear 支持多种与 PDF 相关的操作&#xff0c;包括但不限于编辑、转换、合并和签署 还支持批量操作&#xff0c;支持批量转换功能&#xff0c;可以将 PDF 文件转换为多种格式&#xff0c;包括 Word、Excel、PowerPoint、图像甚至电子书等。 下载方式 请看文章尾部…

虚拟机vaware中cpu设置跑满大核

首先&#xff0c;大核速度快&#xff0c;并且在资源紧张时大核优先&#xff0c;小核甚至是闲着围观大核跑满。其次&#xff0c;遇到经常切换操作虚拟机和win11的使用场景&#xff0c;切换核心本身也会造成一点卡顿&#xff0c;降低虚拟机里操作流畅度。另外&#xff0c;13代在你…