当前位置: 首页 > news >正文

秒懂Linux之共享内存

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

共享内存概念

模拟实现共享内存

创建key阶段

​编辑创建共享内存阶段

删除共享内存阶段 

查看共享内存属性阶段

挂接共享内存到进程阶段

取消共享内存与进程挂接阶段

进程通信阶段

添加管道改进版

共享内存函数

shmget函数

shmat函数

shmdt函数

shmctl函数


共享内存概念

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

之前所学的管道通信本质上是通过内核的文件作为媒介进行通信的,而共享内存则是脱离创建管道的范畴转而到物理内存中让两进程通信~

然而共享内存可以存在很多个,因为不仅仅只有进程A,B需要利用共享内存通信,其他进程之间也需要,所以OS必须管理所有的共享内存~如何管理呢?——先描述,再组织~

那么核心问题来了,如何让不同进程之间看到同一份共享内存呢?前的管道通信是通过创建管道这种特殊文件让不同进程在内核中通过打开同一个文件来建立通信~而在共享内存中进程之间应该如何做到呢?总不能共享内存被创建起那进程A,B就自然而然可以通信了吧?

如果key由OS生成,它哪里知道我们的心思,是无法保证让指定进程看到同一份共享内存的这样倒不如我们自己给一个参数(ftok)来让进程A,B作为同一个参数形成key,这样就可以看到同一份内存了~

模拟实现共享内存

创建key阶段

//设置好生成key的两个参数
const char* pathname = "/home/LJZ";
const int proj_id = 0x66;//转16进制
std::string ToHex(key_t k)
{char buffer[1024];//将k以16进制的格式写入buffersnprintf(buffer,sizeof(buffer),"%x",k);return buffer;
}//获取key函数
key_t GetShmKey()
{//生成keykey_t k = ftok(pathname,proj_id);if (k < 0){std::cerr << "ftok error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;exit(1);}return k;
}
#include "Comm.hpp"int main()
{key_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;return 0;
}

 成功让客户端与服务端都拿到了我们自定义生成的key~



创建共享内存阶段

//创建共享内存函数
int CreatShm(key_t key,int size,int flag)
{//shmget创建共享内存的返回值类似我们创建文件的文件描述符fdint shmid = shmget(key,size,flag);if (shmid < 0){std::cerr << "shmget error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;exit(2);}return shmid;
}//为服务端使用,由服务端来创建共享内存
int SSCreateShm(key_t key, int size)
{// IPC_CREAT: 不存在就创建,存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回return CreatShm(key, size, IPC_CREAT | IPC_EXCL | 0666);
}//为客户端使用,在共享内存存在的情况下获取它
int SCGetShm(key_t key, int size)
{return CreatShm(key, size, IPC_CREAT);
}
int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//获取共享内存int shmid = SCGetShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;return 0;
}
int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;return 0;
}

以上说明在进程结束时如果我们没有主动释放掉共享内存它会一直存在~

ps:ipcrm -m shmid :手动删除共享内存 

删除共享内存阶段 


//删除共享内存函数
void DeleteShm(int shmid)
{//删除共享内存int n = shmctl(shmid,IPC_RMID,nullptr);if (n < 0){std::cerr << "shmctl error" << std::endl;}else{std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;}
}

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;sleep(10);//10s后删除共享内存DeleteShm(shmid);return 0;
}

查看共享内存属性阶段

//查看共享内存属性函数
void ShmDebug(int shmid)
{//shmid_ds里面存储着其信息struct shmid_ds shmds;//将与标识符为 shmid 的共享内存段相关联的内核数据结构中的信息复制到由 buf 所指向的 shmid_ds 结构中int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0){std::cerr << "shmctl error" << std::endl;return;}std::cout << "shmds.shm_segsz: " << shmds.shm_segsz << std::endl;std::cout << "shmds.shm_nattch:" << shmds.shm_nattch << std::endl;std::cout << "shmds.shm_ctime:" << shmds.shm_ctime << std::endl;std::cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << std::endl;
}

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;//查看共享内存信息ShmDebug(shmid);sleep(10);//10s后删除共享内存DeleteShm(shmid);return 0;
}

以上只是为了演示shmctl中模式的多样化选择~

挂接共享内存到进程阶段


//挂接共享内心到进程函数
void *ShmAttach(int shmid)
{//挂接进程,成功则返回已连接的共享内存段的地址。void *addr = shmat(shmid, nullptr, 0);if ((long long int)addr == -1){std::cerr << "shmat error" << std::endl;return nullptr;}return addr;
}

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//获取共享内存int shmid = SCGetShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;sleep(2);//挂接共享内存到客户端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}
int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;//挂接共享内存到服务端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);sleep(100);//10s后删除共享内存DeleteShm(shmid);return 0;
}

有时候我们不想让某一个进程挂接共享内存了,总不能直接把共享内存删掉吧?可以取消它与共享内存的挂接~

取消共享内存与进程挂接阶段

//取消共享内存与进程挂接函数
void ShmDetach(void *addr)
{//通过进程的虚拟地址取消挂接int n = shmdt(addr);if (n < 0){std::cerr << "shmdt error" << std::endl;}
}
int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//获取共享内存int shmid = SCGetShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;sleep(2);//挂接共享内存到客户端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);//客户端取消挂接ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;return 0;
}

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;//挂接共享内存到服务端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);//服务端取消挂接ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
}

所有工作准备完毕,进入通信阶段~


进程通信阶段

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;//挂接共享内存到服务端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;//通信阶段,服务端读取数据//直接打印在共享内存中的数据for(;;){cout << "shm content: " << addr << std::endl;sleep(1);}sleep(100);//服务端取消挂接ShmDetach(addr);sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
}

int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//获取共享内存int shmid = SCGetShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;sleep(2);//挂接共享内存到客户端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);//通信阶段,客户端写入数据//客户端直接在共享内存中写数据memset(addr,0,defaultsize);for (char c = 'A'; c <= 'Z'; c++) {addr[c - 'A'] = c;sleep(1);}sleep(100);//客户端取消挂接ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}

不过这种通信有个无法避免的缺陷:在默认情况中作为shm读取方是不会去阻塞等待写入方的,就一直读取不管有没有写入优点是共享内存这种方式是进程通信中速度最快的,因为写入数据的同时另外一方直接在内存中就可以读取,不需要管道传递的媒介但缺点也很明显,无法提供进程间协同的任何机制,如果我们想要发送一整段字符串时会被这种方式切割为一个个字符读入~

为了能控制想要发送与读取的大小,我们利用管道来实现同步的机制~

添加管道改进版


#define Mode 0666
#define Path "./fifo"//命名管道
class Fifo
{
public:Fifo(const string &path = Path) : _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径+文件名
};//利用管道进行同步数据
class Sync
{
public:Sync() : rfd(-1), wfd(-1){}//以读方式打开管道void OpenRead(){rfd = open(Path, O_RDONLY);if (rfd < 0)exit(1);}//以写方式打开管道void OpenWrite(){wfd = open(Path, O_WRONLY);if (wfd < 0)exit(1);}//让服务端等待管道中写端的写入,到达一定量后再一次性读取bool Wait(){bool ret = true;uint32_t c = 0;ssize_t n = read(rfd, &c, sizeof(uint32_t));//读取的数据大小为uint32_t时提示准备唤醒服务端读取数据代替之前时时刻刻读if (n == sizeof(uint32_t)){std::cout << "server wakeup, begin read shm..." << std::endl;}//代表管道写端关闭,读端读取数据结束,返回0else if (n == 0){ret = false;}else{return false;}return ret;}//往管道写入数据,量够的时候作提示唤醒服务端void Wakeup(){uint32_t c = 0;//往管道写入数据ssize_t n = write(wfd, &c, sizeof(c));//写够数据量的时候,提示唤醒服务端可以读取了assert(n == sizeof(uint32_t));std::cout << "wakeup server..." << std::endl;}~Sync() {}private:int rfd;int wfd;
};
#include "Comm.hpp"
#include <unistd.h>
#include "Fifo.hpp"
int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//获取共享内存int shmid = SCGetShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;sleep(2);//挂接共享内存到客户端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);//通信阶段,客户端写入数据//客户端直接在共享内存中写数据memset(addr,0,defaultsize);Sync syn;//写方式打开管道syn.OpenWrite();for (char c = 'A'; c <= 'Z'; c++) {addr[c - 'A'] = c;sleep(1);//往管道写入数据,写够时发出提示:可唤醒服务端读取数据syn.Wakeup();}sleep(100);//客户端取消挂接ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}


int main()
{//获取keykey_t key = GetShmKey();std::cout << "key: " << ToHex(key) << std::endl;//创建共享内存int shmid = SSCreateShm(key,defaultsize);std::cout << "shmid: " << shmid << std::endl;//挂接共享内存到服务端char * addr = (char*)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(10);//查看共享内存信息//ShmDebug(shmid);// 0. 先引入管道Fifo fifo;Sync syn;//读方式打开管道syn.OpenRead();std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;//通信阶段,服务端读取数据//直接打印在共享内存中的数据for(;;){//读取数据量不够的时候,退出,不让打印(读取)出内容//读取数据量够的时候,打印堆积的内容if(!syn.Wait()) break;cout << "shm content: " << addr << std::endl;sleep(1);}sleep(100);//服务端取消挂接ShmDetach(addr);sleep(20);//10s后删除共享内存DeleteShm(shmid);return 0;
}

管道意义就在于不要让服务端读那么快,而是在管道内积累一定数据量后再一次性读取数据,实现同步的机制。

共享内存函数

shmget函数

功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key: 这个共享内存段名字
size: 共享内存大小
shmflg: 由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr: 指定连接的地址
shmflg: 它的两个可能取值是 SHM_RND SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -1
shmaddr NULL ,核心自动选择一个地址
shmaddr 不为 NULL shmflg SHM_RND 标记,则以 shmaddr 为连接地址。
shmaddr 不为 NULL shmflg 设置了 SHM_RND 标记,则连接的地址会自动向下调整为 SHMLBA 的整数倍。公式: shmaddr -
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY ,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: shmat 所返回的指针
返回值:成功返回 0 ;失败返回 -1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid: shmget 返回的共享内存标识码
cmd: 将要采取的动作(有三个可取值)
buf: 指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回 0 ;失败返回 -1

相关文章:

秒懂Linux之共享内存

目录 共享内存概念 模拟实现共享内存 创建key阶段 ​编辑创建共享内存阶段 删除共享内存阶段 查看共享内存属性阶段 挂接共享内存到进程阶段 取消共享内存与进程挂接阶段 进程通信阶段 添加管道改进版 共享内存函数 shmget函数 shmat函数 shmdt函数 shmctl函数 共享内存概念 共…...

【计算机网络】网络层协议解析

网络层的两种服务IPv4分类编址划分子网无分类地址 IPv4地址应用IP数据报的发送和转发过程主机发送IP数据报路由器转发IP数据报 IPv4数据报首部格式ICMP网际控制报文协议虚拟专用网VPN与网络地址转换NAT 网络层主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传…...

sqlist void reverse(SqList A)

#include <stdlib.h> #include <stdio.h> #include <iostream> using namespace std; #define INIT_SIZE 5 #define INCREMENT 10 # define OK 1 # define ERROR 0/* 定义ElemType为int类型 */ typedef int ElemType; void input(ElemType &s); void out…...

聊聊Thread Local Storage

聊聊ThreadLocal 为什么需要Thread Local StorageThread Local Storage的实现PThread库实现操作系统实现GCC __thread关键字实现C11 thread_local实现JAVA ThreadLocal实现 Thread Local Storage 线程局部存储&#xff0c;简称TLS。 为什么需要Thread Local Storage 变量分为全…...

WEB攻防-JS项目Node.js框架安全识别审计验证绕过

知识点&#xff1a; 1、原生JS&开发框架-安全条件 2、常见安全问题-前端验证&未授权 详细点&#xff1a; 1、什么是JS渗透测试&#xff1f; 在JavaScript中也存在变量和函数&#xff0c;当存在可控变量及函数调用即可参数漏洞 2、流行的Js框架有哪些&#xff1f; …...

STM32——SPI

1.SPI简介 SPI&#xff0c;是英语Serial Peripheral Interface的缩写&#xff0c;顾名思义就是串行外围设备接口。SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xf…...

【云安全】云上资产发现与信息收集

一、云基础设施组件 1、定义 在云计算基础架构中&#xff0c;基础设施组件通常包括&#xff1a;计算、存储、网络和安全等方面的资源。例如&#xff0c;计算资源可以是虚拟机、容器或无服务器计算引擎&#xff1b;存储资源可以是对象存储或块存储&#xff1b;网络资源可以是虚拟…...

flask搭建微服务器并训练CNN水果识别模型应用于网页

一. 搭建flask环境 概念 flask:一个轻量级 Web 应用框架&#xff0c;被设计为简单、灵活&#xff0c;能够快速启动一个 Web 项目。CNN:深度学习模型&#xff0c;用于处理具有网格状拓扑结构的数据&#xff0c;如图像&#xff08;2D网格&#xff09;和视频&#xff08;3D网格&a…...

数据篇| 关于Selenium反爬杂谈

友情提示:本章节只做相关技术讨论, 爬虫触犯法律责任与作者无关。 LLM虽然如火如荼进行着, 但是没有数据支撑, 都是纸上谈兵, 人工智能的三辆马车:算法-数据-算力,缺一不可。之前写过关于LLM微调文章《微调入门篇:大模型微调的理论学习》、《微调实操一: 增量预训练(Pretrai…...

MySQL高阶1890-2020年最后一次登录

目录 题目 准备数据 分析数据 题目 编写解决方案以获取在 2020 年登录过的所有用户的本年度 最后一次 登录时间。结果集 不 包含 2020 年没有登录过的用户。 返回的结果集可以按 任意顺序 排列。 准备数据 Create table If Not Exists Logins (user_id int, time_stamp …...

update-alternatives官方手册

下述手册超链接都是英文&#xff0c;内容差不多&#xff0c;看一个就行 Debian系统的Ubuntu系统的《The Linux Programming Interface》图书上的...

cesium.js 入门到精通(5-2)

在cesium 的配置中 有一些参数 可以配置地图的显示 显示出 水的动态显示 山的效果 相当于一些动画显示的效果 var viewer new Cesium.Viewer("cesiumContainer", {infoBox: false,terrainProvider: await Cesium.createWorldTerrainAsync({requestWaterMask: tru…...

LINUX的PHY抽象层——PAL

英文原文参考&#xff1a; https://www.kernel.org/doc/html/latest/networking/phy.html 中文翻译参考&#xff1a;有关PHY抽象层的总结 https://blog.csdn.net/eydwyz/article/details/124753313 目录 1 前言2 PHY接口模式3 尽量使用PHY端的延时而不是MAC或PCB4 其他方式实现…...

优先级队列(堆)

目录 优先级队列 堆的概念 堆的创建 堆的向下调整 堆的插入 完整代码 优先级队列 队列是一种先进先出的数据结构&#xff0c;有些时候操作的数据可能带有优先级&#xff0c;出队列时就需要优先级高的数据先出队列。 在这种情况下&#xff0c;数据结构应该提供两个最基本…...

帧率和丢帧分析理论

一、丢帧问题概述 应用丢帧通常指的是在应用程序的界面绘制过程中&#xff0c;由于某些原因导致界面绘制的帧率下降&#xff0c;从而造成界面卡顿、动画不流畅等问题。以60Hz刷新率为例子&#xff0c;想要达到每秒60帧&#xff08;即60fps&#xff09;的流畅体验&#xff0c;每…...

solidwork找不到曲面

如果找不到曲面 则右键找到选项卡&#xff0c;选择曲面...

mac安装JetBtains全家桶新版本时报错:Cannot start the IDE

mac安装JetBtains全家桶新版本时报错&#xff1a;Cannot start the IDE 前言报错信息解决方法 前言 作者使用的是Mac电脑&#xff0c;最近想要更新JetBrains相关工具的软件版本&#xff0c;但是在安装时突然报错&#xff0c;导致安装失败&#xff0c;现在将报错信息以及解决方…...

MVCC机制解析:提升数据库并发性能的关键

MVCC机制解析&#xff1a;提升数据库并发性能的关键 MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 多版本并发控制 。 MVCC只在事务隔离级别为读已提交(Read Committed)和可重复读(Repeated Read)下生效。 MVCC是做什么用的 MVCC是为了处理 可重复读 和…...

如何使用Postman搞定带有token认证的接口实战!

现在许多项目都使用jwt来实现用户登录和数据权限&#xff0c;校验过用户的用户名和密码后&#xff0c;会向用户响应一段经过加密的token&#xff0c;在这段token中可能储存了数据权限等&#xff0c;在后期的访问中&#xff0c;需要携带这段token&#xff0c;后台解析这段token才…...

Linux Vim编辑器常用命令

目录 一、命令模式快捷键 二、编辑/输入模式快捷键 三、编辑模式切换到命令模式 四、搜索命令 注&#xff1a;本章内容全部基于Centos7进行操作&#xff0c;查阅本章节内容前请确保您当前所在的Linux系统版本&#xff0c;且具有足够的权限执行操作。 一、命令模式快捷键 二…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...