windows C++:进程间通信高实时性、安全、数据量大的通信方式(一)文件映射 (File Mapping)

        windows进程间通信是写多进程程序的必修课,高实时性、安全、数据量大的通信方式是很必要的,今天我们来看看文件映射

一、文件映射 (File Mapping)

1. 简单的介绍

        文件映射通过将文件的部分或全部内容映射到一个或多个进程的虚拟地址空间,使得这些进程可以像访问普通内存一样访问文件内容。这个过程涉及以下步骤:

  1. 创建或打开文件:进程首先需要创建或打开一个文件。
  2. 创建文件映射对象:通过调用 Windows API 函数 CreateFileMapping,创建一个文件映射对象。这个对象表示文件的映射视图。
  3. 映射视图到内存:使用 MapViewOfFile 函数将文件映射对象的一个视图映射到进程的地址空间中。

2. 文件映射的优势

  1. 高实时性:文件映射将文件内容直接映射到内存,减少了传统 I/O 操作的开销,实现了高效的数据访问。
  2. 高安全性:可以通过设置文件和内存映射对象的权限来控制不同进程对文件的访问。
  3. 处理大数据量:适合处理大数据量的文件,通过内存映射可以高效地进行读写操作。
  4. 共享内存:在同一台计算机上的多个进程可以共享同一块内存,简化了进程间通信的复杂性。

3. 需要用到的API

CreateFile

        用于创建或打开一个文件。

HANDLE CreateFile(LPCSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile
);

参数:

  • lpFileName (LPCSTR): 文件名。
  • dwDesiredAccess (DWORD): 访问权限。常见值包括 GENERIC_READGENERIC_WRITE 等。
  • dwShareMode (DWORD): 共享模式。常见值包括 FILE_SHARE_READFILE_SHARE_WRITE 等。
  • lpSecurityAttributes (LPSECURITY_ATTRIBUTES): 安全属性指针,可为 NULL。
  • dwCreationDisposition (DWORD): 如何创建文件。常见值包括 CREATE_NEWCREATE_ALWAYSOPEN_EXISTING 等。
  • dwFlagsAndAttributes (DWORD): 文件属性和标志。常见值包括 FILE_ATTRIBUTE_NORMALFILE_FLAG_OVERLAPPED 等。
  • hTemplateFile (HANDLE): 模板文件句柄,可为 NULL。

        为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这样可以限制哪些进程可以访问或修改映射的文件内容。

为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这些安全属性允许您指定一个安全描述符,该描述符定义了哪些用户和组可以访问或修改映射的文件内容。以下是如何使用 SECURITY_ATTRIBUTES 结构来设置访问控制权限的步骤:

  1. 创建一个安全描述符:使用 InitializeSecurityDescriptor 函数初始化一个安全描述符。
  2. 设置访问控制列表 (ACL):使用 SetSecurityDescriptorDacl 函数为安全描述符设置一个访问控制列表 (ACL),定义访问权限。
  3. 初始化 SECURITY_ATTRIBUTES 结构:将安全描述符包含在 SECURITY_ATTRIBUTES 结构中。
  4. 传递 SECURITY_ATTRIBUTES 结构:在调用 CreateFileMapping 或其他相关 API 时,将 SECURITY_ATTRIBUTES 结构传递给它们。
    // 定义安全描述符字符串 (SDDL)// 这里设置为允许所有用户读取和写入LPCSTR sddl = "D:P(A;;GA;;;WD)";// 创建一个安全描述符PSECURITY_DESCRIPTOR pSD = NULL;if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(sddl, SDDL_REVISION_1, &pSD, NULL)) {// 处理错误printf("ConvertStringSecurityDescriptorToSecurityDescriptorA 错误: %d\n", GetLastError());return 1;}// 初始化 SECURITY_ATTRIBUTES 结构SECURITY_ATTRIBUTES sa;sa.nLength = sizeof(SECURITY_ATTRIBUTES);sa.lpSecurityDescriptor = pSD;sa.bInheritHandle = FALSE;// 创建或打开文件HANDLE hFile = CreateFile("example.txt",GENERIC_READ | GENERIC_WRITE,0,&sa,  // 使用自定义的安全属性CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

 关键点说明:

  • 安全描述符字符串 (SDDL): 示例中的 SDDL "D:P(A;;GA;;;WD)" 设置了一个允许所有用户读取和写入的安全描述符。您可以根据需求调整这个字符串以设置不同的权限。
  • ConvertStringSecurityDescriptorToSecurityDescriptorA: 将 SDDL 转换为实际的安全描述符。
  • SECURITY_ATTRIBUTES: 包含了安全描述符,用于设置文件和文件映射对象的安全属性。

通过上述步骤和示例代码,您可以设置文件映射的安全属性,控制哪些进程可以访问或修改映射的文件内容。

        总之,文件映射是一种高效、灵活的进程间通信机制,特别适合需要处理大块数据并且要求高实时性和高安全性的场景。通过合理的权限设置和内存管理,可以充分利用文件映射的优势,提高系统的整体性能。

CreateFileMapping

        用于创建一个文件映射对象。

HANDLE CreateFileMapping(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCSTR lpName
);

参数:

  • hFile (HANDLE): 文件句柄。
  • lpFileMappingAttributes (LPSECURITY_ATTRIBUTES): 安全属性指针,可为 NULL。
  • flProtect (DWORD): 保护属性。常见值包括 PAGE_READONLYPAGE_READWRITE 等。
  • dwMaximumSizeHigh (DWORD): 映射文件的最大尺寸(高位)。
  • dwMaximumSizeLow (DWORD): 映射文件的最大尺寸(低位)。
  • lpName (LPCSTR): 文件映射对象的名字,可为 NULL。

返回值: 成功返回文件映射对象句柄,失败返回 NULL。

 

HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,1024,"Local\\MyFileMapping"
);if (hFileMapping == NULL) {// 处理错误
}

MapViewOfFile

        将文件映射对象的一个视图映射到进程的地址空间中。

LPVOID MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,SIZE_T dwNumberOfBytesToMap
);

参数:

  • hFileMappingObject (HANDLE): 文件映射对象句柄。
  • dwDesiredAccess (DWORD): 访问权限。常见值包括 FILE_MAP_READFILE_MAP_WRITE 等。
  • dwFileOffsetHigh (DWORD): 文件偏移量的高位。
  • dwFileOffsetLow (DWORD): 文件偏移量的低位。
  • dwNumberOfBytesToMap (SIZE_T): 映射的字节数。

 返回值: 成功返回映射视图的指针,失败返回 NULL。

LPVOID lpBaseAddress = MapViewOfFile(hFileMapping,FILE_MAP_READ,0,0,0
);if (lpBaseAddress == NULL) {// 处理错误
}

UnmapViewOfFile

        解除文件视图的映射。

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress
);
if (!UnmapViewOfFile(lpBaseAddress)) {// 处理错误
}

CloseHandle

        关闭文件、文件映射对象或文件视图的句柄。

BOOL CloseHandle(HANDLE hObject
);
if (!CloseHandle(hFile)) {// 处理错误
}if (!CloseHandle(hFileMapping)) {// 处理错误
}

4. 安全性

        为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这样可以限制哪些进程可以访问或修改映射的文件内容。

        总之,文件映射是一种高效、灵活的进程间通信机制,特别适合需要处理大块数据并且要求高实时性和高安全性的场景。通过合理的权限设置和内存管理,可以充分利用文件映射的优势,提高系统的整体性能。

二、文件映射底层原理

        在 Windows 底层,文件映射的实现涉及多个关键组件和机制,包括内存管理器、文件系统、内核对象和虚拟内存管理。以下是 Windows 底层实现文件映射的主要步骤和机制:

1. 文件系统与文件句柄

        当应用程序调用 CreateFile 函数时,Windows 内核通过文件系统驱动程序将文件打开并创建一个文件句柄。文件系统驱动程序负责处理文件的底层访问,包括读写操作、权限检查和文件缓存。

2. 创建文件映射对象

        调用 CreateFileMapping 时,Windows 内核创建一个文件映射对象。这涉及到以下几个步骤:

  • 分配内核对象:Windows 内核分配一个内核对象来表示文件映射对象。这个对象包含文件的元数据、权限信息和映射视图的信息。
  • 建立文件与内存的关联:内核为文件映射对象分配一个区域,在文件和虚拟内存之间建立关联。

3. 内存管理器与虚拟内存

        当应用程序调用 MapViewOfFile 时,Windows 内核的内存管理器开始工作:

  • 虚拟地址空间:内存管理器为文件映射对象分配虚拟地址空间。这个虚拟地址空间是应用程序可以访问的内存区域。
  • 页表:内存管理器更新进程的页表,将虚拟地址映射到物理内存。如果物理内存不足,内存管理器会将部分页面换出到分页文件。
  • 懒加载:文件内容不会立即加载到内存。当进程访问映射视图时,内存管理器通过页错误机制将所需的文件内容加载到内存。

4. 共享内存

        多个进程可以通过文件映射对象共享内存。不同进程调用 OpenFileMappingMapViewOfFile 来访问同一个文件映射对象:

  • 内核对象共享:文件映射对象在内核中是全局的,多个进程可以通过内核对象句柄访问同一文件映射对象。
  • 虚拟地址空间独立:虽然虚拟地址空间在每个进程中是独立的,但它们都可以映射到相同的物理内存区域,这实现了内存共享。

5. 文件映射的拆解

        当进程调用 UnmapViewOfFile 时,内存管理器解除文件视图的映射:

  • 页表清理:内存管理器从进程的页表中移除映射条目,释放对应的虚拟地址空间。
  • 引用计数:文件映射对象有一个引用计数,当所有进程都解除映射并关闭文件映射对象时,内核释放文件映射对象和相关的资源。

关键数据结构和函数

  • 文件映射对象:由 FILE_OBJECTSECTION_OBJECT 数据结构表示。
  • 页表(Page Table):内存管理器使用页表来管理虚拟地址到物理地址的映射。
  • 内存管理器函数MmCreateSectionMmMapViewOfSectionMmUnmapViewOfSection 等函数用于管理文件映射的创建、映射和解除映射。

底层调用流程图

graph TD;A[应用程序] -->|调用 CreateFile| B[文件系统驱动]B -->|返回文件句柄| AA -->|调用 CreateFileMapping| C[内核]C -->|创建文件映射对象| D[内存管理器]D -->|建立文件与内存关联| CA -->|调用 MapViewOfFile| DD -->|分配虚拟地址空间| AA -->|访问映射视图| DD -->|通过页错误加载内容| E[物理内存]A -->|调用 UnmapViewOfFile| DD -->|解除映射视图| AC -->|引用计数清零时释放资源| D

三、父子进程间通信的示例

#include <windows.h>
#include <iostream>
#include <string>
#include <stdexcept>
#include <memory>class SharedMemory {
public:SharedMemory(const std::wstring& name, size_t size);~SharedMemory();bool create();bool open();void write(const std::wstring& data);std::wstring read();void close();void signal();  // 用于通知另一个进程数据已写入void wait();    // 用于等待另一个进程写入数据private:std::wstring name_;size_t size_;HANDLE hFile_;HANDLE hMapFile_;LPVOID lpBase_;HANDLE hEvent_;void cleanup();void checkAndThrow(bool condition, const std::wstring& errorMessage);
};// 构造函数
SharedMemory::SharedMemory(const std::wstring& name, size_t size): name_(name), size_(size), hFile_(NULL), hMapFile_(NULL), lpBase_(NULL), hEvent_(NULL) {}// 析构函数
SharedMemory::~SharedMemory() {close();
}// 创建文件映射对象和事件对象
bool SharedMemory::create() {hFile_ = CreateFileW(name_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);checkAndThrow(hFile_ != INVALID_HANDLE_VALUE, L"Unable to create file.");hMapFile_ = CreateFileMappingW(hFile_, NULL, PAGE_READWRITE, 0, static_cast<DWORD>(size_), name_.c_str());checkAndThrow(hMapFile_ != NULL, L"Unable to create file mapping object.");lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");hEvent_ = CreateEventW(NULL, TRUE, FALSE, (name_ + L"_Event").c_str());checkAndThrow(hEvent_ != NULL, L"Unable to create event object.");return true;
}// 打开现有的文件映射对象和事件对象
bool SharedMemory::open() {hMapFile_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name_.c_str());checkAndThrow(hMapFile_ != NULL, L"Unable to open file mapping object.");lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");hEvent_ = OpenEventW(EVENT_ALL_ACCESS, FALSE, (name_ + L"_Event").c_str());checkAndThrow(hEvent_ != NULL, L"Unable to open event object.");return true;
}// 写入数据到映射内存
void SharedMemory::write(const std::wstring& data) {if (lpBase_ != NULL) {memcpy(lpBase_, data.c_str(), (data.size() + 1) * sizeof(wchar_t));  // 包括终止符signal(); // 通知另一个进程数据已写入}
}// 读取数据从映射内存
std::wstring SharedMemory::read() {if (lpBase_ != NULL) {return std::wstring(static_cast<wchar_t*>(lpBase_));}return L"";
}// 关闭文件映射和事件对象
void SharedMemory::close() {cleanup();
}// 用于通知另一个进程数据已写入
void SharedMemory::signal() {if (hEvent_ != NULL) {SetEvent(hEvent_);}
}// 用于等待另一个进程写入数据
void SharedMemory::wait() {if (hEvent_ != NULL) {WaitForSingleObject(hEvent_, INFINITE);}
}void SharedMemory::cleanup() {if (lpBase_ != NULL) {UnmapViewOfFile(lpBase_);lpBase_ = NULL;}if (hMapFile_ != NULL) {CloseHandle(hMapFile_);hMapFile_ = NULL;}if (hFile_ != NULL) {CloseHandle(hFile_);hFile_ = NULL;}if (hEvent_ != NULL) {CloseHandle(hEvent_);hEvent_ = NULL;}
}void SharedMemory::checkAndThrow(bool condition, const std::wstring& errorMessage) {if (!condition) {DWORD errorCode = GetLastError();throw std::runtime_error(std::string(errorMessage.begin(), errorMessage.end()) + " Error code: " + std::to_string(errorCode));}
}void parentProcess() {try {SharedMemory sm(L"MySharedMemory", 1024);if (sm.create()) {std::wcout << L"Shared memory created successfully." << std::endl;STARTUPINFOW si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));si.cb = sizeof(si);ZeroMemory(&pi, sizeof(pi));// 创建子进程if (!CreateProcessW(NULL,   // 子进程可执行文件名const_cast<wchar_t*>(L"ChildProcess.exe child"), // 命令行参数NULL,                  // 进程句柄不可继承NULL,                  // 线程句柄不可继承FALSE,                 // 不继承句柄0,                     // 创建标志NULL,                  // 使用父进程的环境变量NULL,                  // 使用父进程的当前目录&si,                   // 指向 STARTUPINFO 结构体的指针&pi))                  // 指向 PROCESS_INFORMATION 结构体的指针{std::cerr << "Failed to create child process." << std::endl;return;}// 关闭子进程和主线程的句柄CloseHandle(pi.hProcess);CloseHandle(pi.hThread);for (int i = 0; i < 5; ++i) {std::wstring message = L"Message from parent: " + std::to_wstring(i);sm.write(message);sm.signal();std::wcout << L"Parent wrote: " << message << std::endl;sm.wait();std::wstring reply = sm.read();std::wcout << L"Parent read: " << reply << std::endl;// 重置事件以便重复使用ResetEvent(sm.hEvent_);}} else {std::wcerr << L"Failed to create shared memory." << std::endl;}} catch (const std::exception& e) {std::cerr << "Exception: " << e.what() << std::endl;}
}void childProcess() {try {SharedMemory sm(L"MySharedMemory", 1024);if (sm.open()) {for (int i = 0; i < 5; ++i) {sm.wait();std::wstring message = sm.read();std::wcout << L"Child read: " << message << std::endl;std::wstring reply = L"Reply from child: " + std::to_wstring(i);sm.write(reply);std::wcout << L"Child wrote: " << reply << std::endl;sm.signal();// 重置事件以便重复使用ResetEvent(sm.hEvent_);}} else {std::wcerr << L"Failed to open shared memory." << std::endl;}} catch (const std::exception& e) {std::cerr << "Exception: " << e.what() << std::endl;}
}int wmain(int argc, wchar_t* argv[]) {if (argc > 1 && std::wstring(argv[1]) == L"child") {childProcess();} else {parentProcess();}return 0;
}

        先运行父进程 SharedMemoryCommunication.exe(不带参数),它将创建共享内存并启动子进程 SharedMemoryCommunication.exe child。通过这种方式,父进程和子进程可以共享同一份代码,并且通过命令行参数来区分运行时的角色。

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

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

相关文章

Linux-基础IO

&#x1f30e;Linux基础IO 文章目录&#xff1a; Linux基础IO C语言中IO交互       常用C接口         fopen         fputs         fwrite         fgets 当前路径       三个文件流 系统文件IO       open函数     …

什么是Wi-Fi保护设置(WPS),以及如何使用它?这里有详细解释

生活在现代世界的双刃剑是,一切都可以无线连接,但这往往会让我们更容易受到攻击。WPS可以帮助你减轻这种风险,而不需要你精通技术,只需简单地按下路由器上的按钮。 什么是Wi-Fi保护设置(WPS) 当你不想手动连接时,路由器上的WPS按钮是一种连接无线设备的简单方法。它使…

特征模态分解(FMD):一种小众而又新颖的分解方法

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天为大家介绍一个小众而又新颖的信号分…

行业大模型:推动数字化转型的新引擎

引言 随着人工智能技术的飞速发展,大模型技术正成为推动社会进步和产业革新的关键力量。腾讯研究院的《行业大模型调研报告》为我们揭示了这一技术如何催生新一轮的技术创新与产业变革,特别是在工业、金融、广电等领域的数字化转型和高质量发展中发挥着重要作用。 大模型技…

tensorflow实现二分类

# 导入所需库和模块 from tensorflow.keras.layers import Dense, Input, Activation # 导入神经网络层和激活函数模块 from tensorflow.keras.models import Sequential # 导入Keras的Sequential模型 import pandas as pd # 导入Pandas库用于数据处理 import numpy as np …

SQL小练

创建事件 #创建事件&#xff0c;x秒后&#xff0c;用库存更新昨日库存 DELIMITER $$ CREATE EVENT xxx.xxx ON SCHEDULEAT CURRENT_TIMESTAMP INTERVAL 10 SECOND DOBEGINUPDATE stock SET yesterday_quantityquantity;END $$ DELIMITER ;DELIMITER $$ CREATE DE…

接口文档不显示新写的接口

新写的接口&#xff0c;但是不显示&#xff1a; 仔细对比源码才发现没有写tag&#xff1a; 然后就有了&#xff1a;

ES6之正则扩展

正则表达式扩展 u修饰符&#xff08;Unicode模式&#xff09;y修饰符&#xff08;Sticky或粘连模式&#xff09;s修饰符&#xff08;dotAll模式&#xff09;Unicode属性转义正则实例的flags属性字符串方法与正则表达式的整合 javascript的常用的正则表达式 验证数字邮箱验证手机…

C语言中的循环队列与栈、队列之间的转换实现

引言 在数据结构的学习中&#xff0c;栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;是两个非常重要的概念。它们分别遵循着后进先出&#xff08;LIFO&#xff09;和先进先出&#xff08;FIFO&#xff09;的原则。在某些情况下&#xff0c;我们可能需要…

C++——超简单登录项目

程序入口文件 #include <QtWidgets/QApplication> // 包含登录页面头文件 #include "DlgLogin.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);// 程序入口// 调页面起来//DlgMain w;//w.show();// 换成登录页面DlgLogin w;w.show();return…

QT状态机6-无目标切换

一个切换也可以没有目标状态,一个没有目标状态的切换也可以像其他切换那样被触发。 其不同之处在于,当一个没有目标的切换被触发时,它不会引起任何的状态变化, 这样便可以让状态机在一个特定的状态时响应信号或者事件而不用离开这个状态。 回顾之前的学习,如下所示:当我…

开源禅道zentao的使用

很不幸禅道因为漏洞被人进攻了&#xff0c;被迫研究。 1.安装 直接使用docker进行部署&#xff0c;这里有非常多门道。官网的镜像easysoft-zentao是属于docker安装&#xff0c;而idoop的镜像虽然也是docker安装&#xff0c;但是实际是使用官网linux一键安装的版本&#xff0c…

一周学习总结:数组与链表

学习内容&#xff1a;数组与链表、计算机网络知识 数组&#xff1a; 从数组的基础知识到相关应用 数组的基础知识&#xff1a;数组在内存中的存储、数组的相关操作&#xff08;获取与更新&#xff09;、数组的相关应用&#xff1a; 二分查找法⭐⭐⭐⭐⭐ ● 掌握左闭右闭的…

2024第16届四川教育后勤装备展6月1日举办 欢迎参观

2024第16届四川教育后勤装备展6月1日举办 欢迎参观 邀请函 主办单位&#xff1a; 中国西部教体融合博览会组委会 承办单位&#xff1a;重庆港华展览有限公司 博览会主题&#xff1a;责任教育 科教兴邦 组委会&#xff1a;交易会159交易会2351交易会9466 展会背景 成都…

[报告购买] 2021新版知识付费行业报告-艾媒咨询,教育产品运营体系下,如何利用平台寻找私域流量的出路?

对教育机构来说&#xff0c;现在既是一个流量充沛的时代&#xff0c;又是一个流量稀缺的时代。互联网环境下&#xff0c;各平台流量数以亿计&#xff0c;为了获客&#xff0c;各教育品牌机构投放、冠名、代言、竞价已成常态化&#xff0c;公域平台的流量争夺如火如荼。 一轮一轮…

Spring之bean的细节(创建方式、作用范围、生命周期)

在Spring框架中&#xff0c;Bean是一个非常重要的概念&#xff0c;它代表了应用程序中需要被管理的对象。关于Bean的细节&#xff0c;我们可以从创建方式、作用范围以及生命周期三个方面进行阐述。 创建方式 Spring支持以下三种方式创建Bean&#xff1a; 调用构造器创建Bean…

Chatgpt教你使用Python开发iPhone风格计算器

上次使用Chatgpt写爬虫&#xff0c;虽然写出来的代码很多需要修改后才能运行&#xff0c;但Chatgpt提供的思路和框架都是没问题。 这次让Chatgpt写一写GUI程序&#xff0c;也就是你常看到的桌面图形程序。 由于第一次测试&#xff0c;就来个简单点的&#xff0c;用Python写用…

GPU Burn测试指导

工具下载链接&#xff1a; https://codeload.github.com/wilicc/gpu-burn/zip/master测试方法&#xff1a; 上传工具到操作系统下&#xff0c;解压缩工具&#xff0c;使用make命令完成编译&#xff08;确保cuda环境变量已经配置成功、 nvcc -v能显示结果&#xff09;。 如果安…

文献速递:多模态深度学习在医疗中的应用--多模式婴儿脑分割技术:模糊引导深度学习

Title 题目 Multimodal Infant Brain Segmentation by Fuzzy-informed Deep Learning 多模式婴儿脑分割技术&#xff1a;模糊引导深度学习 01 文献速递介绍 日益普及的非侵入式婴儿脑磁共振图像&#xff08;MRI&#xff09;为准确理解脑主要发展轨迹的动态性提供了机会&…

树莓派|串口通信协议

1、串口通信原理 串口通讯(Serial Communication)&#xff0c;是指外设和计算机间&#xff0c;通过数据信号线、地线等&#xff0c;按位进行传输数据的一种通讯方式。串口是一种接口标准&#xff0c;它规定了接口的电气标准&#xff0c;没有规定接口插件电缆以及使用的协议。串…