【共享内存】System V共享内存{通信原理/相关接口/代码测试}
文章目录
- 1.初识共享内存
- 1.0浅谈System V
- 1.1什么是共享内存?
- 1.2Linux-System V共享内存
- 1.3图解共享内存
- 1.4对共享内存的理解
- 2.创建共享内存
- 2.1共享内存如何创建?
- 2.2代码运行与测试
- 2.3shm与pipe的区别
- 2.4shm缺乏访问控制
- 3.代码理解shm
- 3.1Log.hpp
- 3.2common.hpp
- 3.3shmServer.cpp
- 3.4shmClient.cpp
1.初识共享内存
1.0浅谈System V
在Linux下一切皆文件的情况下,这套模式并不太好。
- 使用复杂
- System V的一套接口是OS单独拉出来的一个模块,在大项目中不易于集成化,需要单独特殊化处理。【但仍与文件有关 】
1.1什么是共享内存?
共享内存是Unix系统下的多进程间的通信方法,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。它允许多个不相关的进程访问同一个物理内存区域,从而使它们能够相互通信和共享数据。这是进程间通信中最简单的方式之一。
共享内存允许多个进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。这使得共享内存成为进程间共享数据的一种最快的方法。
然而,由于多个进程可以同时访问共享内存,因此需要某种同步机制(如互斥锁和信号量)来避免对共享内存的冲突访问。当一个进程正在向共享内存区写数据时,其他进程在数据被写入完成前不应该去读或写这些数据。
共享内存的使用通常包括以下步骤:
创建或打开共享内存对象。
将指定的共享内存映射到进程的地址空间,以便进行访问。
在完成通信后,撤销对共享内存的映射。
删除共享内存对象。
需要注意的是,共享内存是在多个进程之间共享一段内存区域,因此,对于这段内存的访问和管理需要谨慎处理,以避免出现数据不一致或冲突的情况。
总的来说,共享内存是一种高效的进程间通信方式,它允许多个进程直接读写同一块内存区域,而不需要进行数据的拷贝,从而大大提高了通信的效率。但是,它也需要适当的同步机制来确保对共享内存的正确访问。
1.2Linux-System V共享内存
Linux下的System V共享内存是Unix系统V版本引入的一种进程间通信(IPC)机制。它允许多个进程共享同一块物理内存区域,以实现高效的数据交换和通信。
在System V共享内存中,进程通过系统调用(如shmget、shmat、shmdt等)来创建、映射和解除映射共享内存段。这些系统调用提供了对共享内存的管理和操作功能。
具体来说,shmget函数用于创建或获取一个共享内存段的标识符。它需要提供一个唯一的键值(key)来标识共享内存段,并指定共享内存的大小和访问权限。成功时,shmget返回一个共享内存标识符(shmid),失败时返回-1。
shmat函数用于将共享内存段映射到进程的地址空间中。它接受共享内存标识符(shmid)和可选的映射地址作为参数。如果映射地址为空,则系统会自动选择一个合适的地址进行映射。成功时,shmat返回映射后的地址,失败时返回-1。
一旦共享内存映射到进程的地址空间,多个进程就可以通过直接读写这块内存区域来进行通信。由于共享内存是直接在物理内存中分配的,因此它的访问速度非常快,是进程间通信中最快的方式之一。
需要注意的是,多个进程同时访问共享内存时,可能会出现数据竞争和不一致的问题。因此,需要使用同步机制(如信号量)来确保对共享内存的正确访问。
当不再需要共享内存时,可以使用shmdt函数来解除映射,并使用shmctl函数来删除共享内存段。这些操作可以确保资源被正确释放,避免内存泄漏和不必要的资源浪费。
总的来说,Linux下的System V共享内存是一种高效的进程间通信机制,它允许多个进程直接读写同一块内存区域,而不需要进行数据的拷贝和传输。然而,由于共享内存访问的复杂性,需要谨慎处理同步和并发访问的问题。
1.3图解共享内存
- 共享内存和动态库都被映射到进程地址空间的共享区
- 通信原理图
1.4对共享内存的理解
- 共享内存提供者,是操作系统
- 操作系统要管理共享内存 ==> 先描述在组织
- 共享内存 == 共享内存块 + 共享内存的内核数据结构
- 对共享内存的管理转变成对数据结构的增删查改
OS怎么知道:
- 如何知道是否需要释放这块内存时
- 当前有多少进程和共享内存关联?
- 这块内存有多大?
- 如何禁止一些进程访问这块内存(权限问题)?
需要知道该内存的一系列属性信息 ⇒ 通过内核数据结构获取属性信息
2.创建共享内存
共享内存是有很多个的 类似于这样
2.1共享内存如何创建?
假设A进程(server)与B进程(client)通信
A进程调用shmget()接口
返回值
共享内存的用户层标识符(类似fd但不同)
key
- 通过调用ftok() 获取的系统中唯一的一个值(数值是几不重要,只要唯一即可–>用来唯一标识指定共享内存块的)
- A创建了共享内存块,B怎么看到这个共享内存块,如何保证二者可以访问同一块资源呢?AB通过唯一的key在内核中访问同一个共享内存块 ⇒ 看到了同一块“资源”
- AB如何获取同一个key?AB调用同一个ftok()根据相同的参数通过某种算法计算出同一个key(这个经过算法加算出来的key一般在内核中是唯一的,存在出错的情况,此处不讨论)
- A调用ftok()获取一个key,在内存中创建一个共享内存块,以此key标识这个shm(将此key存入该shm的数据结构中),B通过ftok()相同的参数相同的算法获取相同的key,根据这个key,去内核中寻找对应的shm,至此,AB两个进程看到了同一块“资源”
size
创建的shm的大小
共享内存的大小 最好是页(PAGE: 4096)的整数倍 4kb
OS管理物理内存时 page的大小以4kb为单位
若申请4097 OS会开辟2个page【对page向上取整】
而第二个page的4095 申请shm的人无法使用 OS其他进程也无法使用 ==> 大大的浪费/zz行为
shmflg
一般有两个选项
IPC_CREAT单独使用:创建shm时,若底层已经存在想要创建的shm,获取并返回shmid;若不存在,创建并返回shmid
IPC_EXCL单独使用:无意义
IPC_CREAT and IPC_EXCL:若底层不存在,创建并返回shmid;若底层存在想要创建的shm,出错返回。⇒ 只要返回成功,此时使用的一定是一个全新的shm
ftok()
shmctl
建立/删除映射
shmid:要将哪个共享内存建立映射
shmaddr:要建立映射的虚拟地址 不建议指定 传空指针让OS帮我们建立映射
shmflg:可以传参以只读方式挂接等
shmid: 这是一个共享内存标识符,通常是通过 shmget 函数获得的。
nullptr: 这是一个指向 shmat 应该放置附加内存的起始地址的指针。由于这里传入的是 nullptr,内核将自动选择一个合适的地址。
0: 这是一个标志位,用于控制共享内存的访问权限和附加方式。在这里,0 表示使用默认的读/写权限。
返回值:
shmaddr: 这是一个指向附加的共享内存段的指针。如果 shmat 调用成功,shmaddr 将指向共享内存段的起始地址。如果调用失败,shmaddr 将是 (char *) -1,并且 errno 将被设置为指示错误原因的值。
注意:在实际代码中,你应该检查 shmaddr 是否为 (char *) -1 来确定 shmat 调用是否成功,并相应地处理任何错误。
另外,请注意,shmat 的返回值类型是 void*,但在这个例子中,它被强制转换为 char*。这样做是为了方便进行指针算术和内存访问,因为 char 类型的大小是 1 字节,所以 char* 指针可以逐字节地遍历内存。
与malloc的使用类似
int shmid = shmget(k, SHM_SIZE, 0);
shmget 是一个在 Unix 和类 Unix 系统(如 Linux)中用于创建或获取共享内存段的系统调用。共享内存是多个进程间通信的一种方式,允许多个进程共享同一块内存区域。
k: 这是共享内存段的键(key)。通常,这是一个整数,用于唯一标识共享内存段。如果键的值为 IPC_PRIVATE,那么将创建一个新的、唯一的共享内存段。
SHM_SIZE: 这是你希望创建的共享内存段的大小(以字节为单位)。如果共享内存段已经存在,这个值将被忽略,但通常你会希望它与你期望的大小相匹配。
第三个参数是共享内存段的权限标志。当你传递 0 作为这个参数时,你实际上没有指定任何权限标志。那么函数将尝试获取一个已经存在的共享内存段,并且不会检查或修改其权限。
函数的返回值 shmid 是一个整数,代表共享内存段的标识符。如果函数成功,它将返回这个标识符;如果失败,它将返回 -1。
int shmid = shmget(k, SHM_SIZE, 0);
这行代码尝试获取一个与键 k 关联的共享内存段,如果它不存在并且没有 IPC_CREAT 标志,则调用将失败。返回的标识符(如果成功)将存储在 shmid 变量中。
shmid vs key
shmid: 用户层标定shm唯一性
key:内核层面上唯一标识shm的
只创建时用key,大多数情况用户访问shm用shmid
【匿名/命名管道等基于文件的生命周期随进程】
当进程结束,shm还在吗?
若不显示删除,sm仍然存在,System V shm生命周期随内核。
怎么删?命令行命令删/代码调用接口删(建议第二种)
2.2代码运行与测试
while : ; do ipcs -m ; sleep 1 ; done
物理内存中的shm映射到进程地址空间后,进程地址空间的shm属于内核空间还是用户空间?
映射到虚拟地址空间的shm属于用户空间,当AB两个进程将同一个物理内存的shm映射到各自的PAS(Process Address Space),AB进程通信时,直接进行内存级的读写即可,无需经过其他调用。
匿名管道/命名管道中的pipe/fifo为什么要通过调用系统接口read/write来进行通信?
pipe/fifo都是文件,是OS进行维护和管理的,而映射到PAS的shm相当于一个进程的
以上的工作:创建key/创建shm/映射shm/删除映射/删除shm是在干嘛?
让不同的进程看到同一份“资源”(内存)
2.3shm与pipe的区别
shm在通信方法中速度较快
pipe的通信方式
shm的通信方式
2.4shm缺乏访问控制
- 只要是通信双方使用shm,一方直接向共享内存中写入数据,另一方就可以立马看到对方写入的数据。
共享内存是所有进程间通信(IPC),速度最快的,不需要过多的拷贝(不需要将数据给操作系统) - 共享内存缺乏访问控制 会带来并发问题
缺乏访问控制导致:写入方写了一部分数据 读取方就读走了并对这半个数据进行了处理得到了错误的结果
写入方不在/写入方不写 读取方仍在读 双方压根不知道对方的存在 即写入方只知道一味的写 读取方只知道一味的读
3.代码理解shm
3.1Log.hpp
#ifndef _LOG_H_
#define _LOG_H_#include <iostream>
#include <ctime>#define Debug 0
#define Notice 1
#define Warning 2
#define Error 3const std::string tip[] = {"Debug","Notice","Warning","Error"};std::ostream &Log(std::string message, int option)
{// 获取时间戳 time_t timestamp; time(timestamp);time_t timestamp = time(nullptr);if (timestamp == std::time_t(-1)){std::cerr << "获取时间失败" << std::endl;exit(1);}// 获取格式化时间 tm *localtime(const time_t *__timer)tm *timeinfo = std::localtime(×tamp);std::cout << " | "<< 1900 + timeinfo->tm_year << "-"<< 1 + timeinfo->tm_mon << "-"<< timeinfo->tm_mday << " "<< timeinfo->tm_hour << ":"<< timeinfo->tm_min << ":"<< timeinfo->tm_sec<< " | "<< tip[option]<< " | "<< message;return std::cout;
}#endif
3.2common.hpp
#pragma once#include <iostream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"using namespace std; // 实际项目中 不展开#define PATH_NAME "/home/lhr/linux" // 此路径一定要有访问权限
#define PROJ_ID 0x66 // 0~255
#define SHM_SIZE 4096 // 共享内存的大小 最好是页(PAGE: 4096)的整数倍 4kb
#define FIFO_NAME "./fifo" // 当前路径下创建一个fifo管道文件string ConvertToHex(key_t k)
{char buffer[32];snprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}class Init
{
public:Init(){umask(0);int n = mkfifo(FIFO_NAME, 0666);assert(n == 0);(void)n;Log("create fifo success", Notice) << std::endl;}~Init(){unlink(FIFO_NAME);Log("remove fifo success", Notice) << std::endl;}
};#define READ O_RDONLY
#define WRITE O_WRONLYint OpenFIFO(std::string pathname, int flags)
{int fd = open(pathname.c_str(), flags);assert(fd >= 0);return fd;
}//服务端调用read去阻塞等待数据 直到有人向管道写数据 等待停止
void Wait(int fd)
{//no_use仅仅是实现“读数据”这一动作的对象 其中的数据无意义Log("等待中....", Notice) << std::endl;//read接口从fd读存到tmp 无数据则阻塞等待uint32_t no_use = 0;ssize_t s = read(fd, &no_use, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;
}//客户端调用write向管道写数据 此动作使得管道中添加数据 服务端停止等待
void Wake(int fd)
{//no_use仅仅是实现“写数据”这一动作的对象 其中的数据无意义uint32_t no_use = 1;ssize_t s = write(fd, &no_use, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;Log("唤醒中....", Notice) << std::endl;
}void CloseFifo(int fd)
{close(fd);
}
3.3shmServer.cpp
#include "common.hpp"// 程序加载时自动构建全局对象 -- 调用构造函数创建管道文件
// 程序退出时自动析构全局对象 -- 调用析构函数删除管道文件
Init init;int main()
{// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << ConvertToHex(k) << endl;// 2. 创建shm shm创建成功后会自动全部置0// server是通信的发起者 建议创建一个全新的共享内存int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1){perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;//sleep(10);// 3. 将指定的共享内存 挂接到自己的地址空间char *shmaddr = (char *)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " shmid : " << shmid << endl;//sleep(10);// 开始进行通信 将共享内存看成一个大字符串 char buffer[SHM_SIZE];int fd = OpenFIFO(FIFO_NAME, READ);while (true){Wait(fd);// 临界区printf("%s\n", shmaddr);if (strcmp(shmaddr, "quit") == 0)break;}CloseFifo(fd);// 4. 将指定的共享内存 从自己的地址空间中删除映射int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm done", Debug) << " shmid : " << shmid << endl;//sleep(10);// 5. 删除共享内存 IPC_RMID: 即便是有进程和当下的shm挂接 依旧删除共享内存n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;Log("delete shm done", Debug) << " shmid : " << shmid << endl;return 0;
}
3.4shmClient.cpp
#include "common.hpp"int main()
{Log("Client pid is : ", Debug) << getpid() << endl;// 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID); // typedef int key_tif (k < 0){Log("create key failed", Error) << " client key : " << ConvertToHex(k) << endl;exit(1);}Log("create key done", Debug) << " client key : " << ConvertToHex(k) << endl;// 获取共享内存int shmid = shmget(k, SHM_SIZE, 0);if (shmid < 0){Log("obtain shm failed", Error) << " shmid : " << shmid << endl;exit(2);}Log("obtain shm success", Debug) << " shmid : " << shmid << endl;//sleep(10);// 将指定的共享内存 挂接到自己的地址空间char *shmaddr = (char *)shmat(shmid, nullptr, 0);if (shmaddr == nullptr){Log("attach shm failed", Error) << " shmid : " << shmid << endl;exit(3);}Log("attach shm success", Debug) << " shmid : " << shmid << endl;//sleep(10);// 开始通信 client将共享内存看做一个char 类型的bufferint fd = OpenFIFO(FIFO_NAME, WRITE);while (true){ssize_t s = read(0, shmaddr, SHM_SIZE - 1); // 从stdin读数据存到shmif (s > 0){shmaddr[s - 1] = 0; // abcd\n ==> abcd0Wake(fd);if (strcmp(shmaddr, "quit") == 0)break;}}CloseFifo(fd);/* 每一次都向共享内存的起始地址写入char *msg = "hello server, I'm Client. my pid: %d, inc: %c\n";for (char a = 'a'; a <= 'z'; a++){shmaddr[a - 'a'] = a; // 直接当成字符数组来看待snprintf(shmaddr, SHM_SIZE - 1, msg, getpid(), a); // 库函数格式化输入数据sleep(1);}strcpy(shmaddr, "quit");*/// 删除映射int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm success", Debug) << " shmid : " << shmid << endl;//sleep(10);// client不需要chmctl删除return 0;
}
相关文章:

【共享内存】System V共享内存{通信原理/相关接口/代码测试}
文章目录 1.初识共享内存1.0浅谈System V1.1什么是共享内存?1.2Linux-System V共享内存1.3图解共享内存1.4对共享内存的理解 2.创建共享内存2.1共享内存如何创建?2.2代码运行与测试2.3shm与pipe的区别2.4shm缺乏访问控制 3.代码理解shm3.1Log.hpp3.2comm…...

Web渗透测试流程
什么是渗透测试 渗透测试 (penetration test),是通过模拟恶意黑客的攻击方法,来评估计算机网络系统安全的一种评估方法。这个过程包括对系统的任何弱点、技术缺陷或漏洞的主动分析,这个分析是从一个攻击者可能存在的位置来进行的,并且从这个…...

探索机器学习的无限可能性:从初学者到专家的旅程
探索机器学习的无限可能性:从初学者到专家的旅程 在当今数字时代,机器学习无疑是最引人注目的技术之一。它已经深入到我们生活的方方面面,从个性化推荐到自动驾驶汽车,再到医疗诊断和金融预测。但是,即使我们已经见证…...

【python】六个常见爬虫案例【附源码】
大家好,我是博主英杰,整理了几个常见的爬虫案例,分享给大家,适合小白学习 一、爬取豆瓣电影排行榜Top250存储到Excel文件 近年来,Python在数据爬取和处理方面的应用越来越广泛。本文将介绍一个基于Python的爬虫程序&a…...

Java零基础-多维数组
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...

Linux网络套接字之UDP网络程序
(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 实现一个简单的对话发消息的功能! 目录…...

Apache POI 解析和处理Excel
摘要:由于开发需要批量导入Excel中的数据,使用了Apache POI库,记录下使用过程 1. 背景 Java 中操作 Excel 文件的库常用的有Apache POI 和阿里巴巴的 EasyExcel 。Apache POI 是一个功能比较全面的 Java 库,适合处理复杂的 Offi…...
SQL 注入攻击 - insert注入
环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、注入原理 描述:insert注入是指通过前端注册的信息被后台通过insert操作插入到数据库中。如果后台没有做相应的处理,就可能导致insert注入漏洞。原因:后台未对用户输入进行充…...

第一个 Angular 项目 - 添加路由
第一个 Angular 项目 - 添加路由 前置项目是 第一个 Angular 项目 - 添加服务,之前的切换页面使用的是 ngIf 对渲染的组件进行判断,从而完成渲染。这一步的打算是添加路由,同时添加 edit recipe 的功能(同样通过路由实现) 用到的内容为&…...
如何简洁高效的搭建一个SpringCloud2023的maven工程
前言 依赖管理有gradle和maven,在这里选择比较常用和方便的Maven作为工程项目和依赖管理工具来搭建SpringCloud实战工程。主要用到的maven管理方式是多模块和bom依赖管理。 什么是maven的多模块依赖管理 Maven 多模块项目相对于单模块项目而言,依赖是…...

uniapp直接连接wifi(含有ios和安卓的注意事项)
前言 小程序中直接连接wifi-----微信小程序 代码 启动 //启动wifistartWifi() {return new Promise((resolve, reject) > {uni.startWifi({success: (res) > {console.log(启动wifi 成功, res)resolve(true)},fail: (err) > {console.error(启动wifi 失败, err)uni.s…...
一. Ubuntu入门
目录 一. Ubuntu系统安装 1. 安装虚拟机软件VMware 2. 安装Ubuntu操作系统 二. Ubuntu系统入门 1. Shell操作 1.1 Shell 简介 1.2 Shell基本操作 1.3 常用Shell命令 (1) 目录信息查看命令ls (2) 目录切换命令cd (3) 当前路径显示命令pwd (4) 系统信息查看命令uname…...
rk3568 Android12 增加支持 ntfs 格式
rk3568 Android12 增加支持 ntfs 格式 Windows平台上可移动硬盘支持 NTFS,FAT32,exFAT三种格式。Fat32文件格式是一种通用格式,任何USB存储设备都会预装该文件系统,可以在任何操作系统平台上使用。最主要的缺陷是只支持最大单文件大小容量为4GB,因此日常使用没有问题,只有…...
【MapReduce】02.Hadoop序列化
实现bean对象序列化步骤 自定义bean对象实现序列化接口。 1)必须实现Writable接口 2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造 public FlowBean(){super(); } 3)重写序列化方法 Override public …...

【Claude 3】一文谈谈Anthropic(Claude) 亚马逊云科技(Bedrock)的因缘际会
文章目录 前言1. Anthropic的诞生2. Anthropic的“代表作”——Claude 3的“三驾马车”3. 亚马逊云科技介绍4. 强大的全托管服务平台——Amazon Bedrock5. 亚马逊云科技(AWS)和Anthropic的联系6. Claude 3模型与Bedrock托管平台的关系7. Clude 3限时体验入口分享【⚠️截止3月1…...
c#开发100问?
什么是C#?C#是由谁开发的?C#与Java之间有哪些相似之处?C#与C有哪些不同之处?C#的主要特性是什么?请解释C#中的类和对象。C#中的命名空间是什么?什么是C#中的属性和字段?请解释C#中的继承和多态性…...

回归预测 | Matlab实现BiTCN-BiGRU-Attention双向时间卷积双向门控循环单元融合注意力机制多变量回归预测
回归预测 | Matlab实现BiTCN-BiGRU-Attention双向时间卷积双向门控循环单元融合注意力机制多变量回归预测 目录 回归预测 | Matlab实现BiTCN-BiGRU-Attention双向时间卷积双向门控循环单元融合注意力机制多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.M…...

SpringCloud微服务-RabbitMQ快速入门
文章目录 RabbitMQ快速入门1、什么是MQ?2、RabbitMQ概述3、RabbitMQ的结构和概念4、常见消息模型5、HelloWorld RabbitMQ快速入门 1、什么是MQ? MQ (MessageQueue),中文是消息队列,字面来看就是存放消息的…...

OpenCV学习笔记(五)——图片的缩放、旋转、平移、裁剪以及翻转操作
目录 图像的缩放 图像的平移 图像的旋转 图像的裁剪 图像的翻转 图像的缩放 OpenCV中使用cv2.resize()函数进行缩放,格式为: resize_imagecv2.resize(image,(new_w,new_h),插值选项) 其中image代表的是需要缩放的对象,(new_w,new_h)表…...

c++ 串口通信库
根据资料整理的串口通信库,封装成为了动态库,使用者只需要调用接口即可 使用实例如下: //接受数据 void CSerialPortCommonLibDemoDlg::OnReceive() { char * str NULL; str new char[256]; _port.readAllData(str); CString s…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...