C++并发编程之跨应用程序与驱动程序的单生产者单消费者队列
设计一个单生产者单消费者队列(SPSC队列),不使用C++ STL库或操作系统原子操作函数,并且将其放入跨进程共享内存中以便在Ring3(用户模式)和Ring0(内核模式)之间传递数据,是一个复杂的任务。这通常涉及到内核驱动开发和用户态程序设计的深入知识。
以下是一个简化的设计概述,用于指导如何构建这样的队列:
1. 定义队列结构
首先,你需要定义一个队列数据结构,它将存储在共享内存中。这个结构应该包含队列的头部和尾部指针,以及用于存储数据的缓冲区。
typedef struct QueueItem {// 数据字段,根据需要定义char data[DATA_SIZE];
} QueueItem;typedef struct SPSCQueue {volatile unsigned int head; // 队列头指针volatile unsigned int tail; // 队列尾指针QueueItem items[QUEUE_SIZE]; // 队列数据项
} SPSCQueue;
2. 共享内存创建与映射
在Ring3(用户态)和Ring0(内核态)之间共享内存通常涉及到创建一个内存映射区域,这可以通过操作系统提供的机制来完成,例如在Windows上可以使用内存映射文件。
在内核态,你需要使用特定的内核API来映射这块内存,以便内核态的代码和用户态的代码可以访问同一块内存区域。
3. 同步机制
由于不能使用原子操作函数,你需要实现一种简单的同步机制来确保生产者和消费者之间的正确交互。一种可能的方法是使用一个简单的自旋锁或者信号量机制。
例如,你可以使用一个标志位来表示队列是否正在被访问:
typedef struct SPSCQueue {volatile unsigned int head;volatile unsigned int tail;volatile bool isLocked; // 表示队列是否被锁定的标志位QueueItem items[QUEUE_SIZE];
} SPSCQueue;
生产者和消费者在操作队列之前都需要检查isLocked
标志位。
4. 生产者逻辑
生产者负责将数据放入队列中。它应该遵循以下逻辑:
- 检查队列是否已满。
- 如果队列未满且未被锁定,则锁定队列,并添加数据到队列尾部。
- 更新尾部指针,并解锁队列。
5. 消费者逻辑
消费者负责从队列中取出数据。它应该遵循以下逻辑:
- 检查队列是否为空。
- 如果队列非空且未被锁定,则锁定队列,并从队列头部取出数据。
- 更新头部指针,并解锁队列。
6. 注意事项
- 内存对齐:确保共享内存中的数据结构对齐,以避免潜在的内存访问问题。
- 错误处理:添加适当的错误处理逻辑,以处理共享内存创建失败、队列满或空等异常情况。
- 性能优化:由于不能使用原子操作,自旋锁可能会导致CPU资源的浪费。在实际应用中,可能需要考虑更高效的同步机制。
7. 跨Ring3/Ring0通信
在Ring3和Ring0之间通信时,需要确保内核态和用户态都能正确地访问和修改共享内存中的队列。在内核态中,你可能需要编写特定的驱动程序来处理这些操作,并确保安全性。
总的来说,这个设计是一个高级概述,具体实现将取决于你的操作系统和环境。在开发过程中,需要深入了解操作系统的内存管理、进程间通信以及内核态与用户态交互的细节。
根据前述设计思想实现一个单生产者单消费者队列(SPSC队列),并将其放入跨进程共享内存中,以便在Ring3(用户态)和Ring0(内核态)之间传递数据。这个实现将分为用户态(Ring3)和内核态(Ring0)两个部分。
用户态(Ring3)实现
用户态代码负责创建和映射共享内存,并实现生产者和消费者的逻辑。
1. 定义队列结构
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>#define QUEUE_SIZE 1024
#define DATA_SIZE 64typedef struct QueueItem {char data[DATA_SIZE];
} QueueItem;typedef struct SPSCQueue {volatile unsigned int head;volatile unsigned int tail;volatile bool isLocked;QueueItem items[QUEUE_SIZE];
} SPSCQueue;
2. 创建和映射共享内存
SPSCQueue* create_shared_memory(const char* name) {int fd = shm_open(name, O_CREAT | O_RDWR, 0666);if (fd == -1) {perror("shm_open");exit(EXIT_FAILURE);}if (ftruncate(fd, sizeof(SPSCQueue)) == -1) {perror("ftruncate");close(fd);exit(EXIT_FAILURE);}SPSCQueue* queue = (SPSCQueue*) mmap(NULL, sizeof(SPSCQueue), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (queue == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}close(fd);return queue;
}void destroy_shared_memory(const char* name, SPSCQueue* queue) {if (munmap(queue, sizeof(SPSCQueue)) == -1) {perror("munmap");exit(EXIT_FAILURE);}if (shm_unlink(name) == -1) {perror("shm_unlink");exit(EXIT_FAILURE);}
}void initialize_queue(SPSCQueue* queue) {queue->head = 0;queue->tail = 0;queue->isLocked = false;memset(queue->items, 0, sizeof(queue->items));
}
3. 生产者逻辑
void producer(SPSCQueue* queue) {while (1) {while (queue->isLocked) {// 自旋等待,直到队列解锁}// 检查队列是否已满if ((queue->tail + 1) % QUEUE_SIZE == queue->head) {printf("Queue is full, skipping...\n");continue;}queue->isLocked = true;// 写入数据snprintf(queue->items[queue->tail].data, DATA_SIZE, "Data %u", queue->tail);queue->tail = (queue->tail + 1) % QUEUE_SIZE;printf("Produced: %s\n", queue->items[(queue->tail + QUEUE_SIZE - 1) % QUEUE_SIZE].data);queue->isLocked = false;// 模拟生产时间sleep(1);}
}
4. 消费者逻辑
void consumer(SPSCQueue* queue) {while (1) {while (queue->isLocked) {// 自旋等待,直到队列解锁}// 检查队列是否为空if (queue->head == queue->tail) {printf("Queue is empty, waiting...\n");continue;}queue->isLocked = true;// 读取数据char data[DATA_SIZE];strncpy(data, queue->items[queue->head].data, DATA_SIZE);queue->head = (queue->head + 1) % QUEUE_SIZE;printf("Consumed: %s\n", data);queue->isLocked = false;// 模拟消费时间sleep(1);}
}
5. 主函数
int main() {const char* shm_name = "/spsc_queue";SPSCQueue* queue = create_shared_memory(shm_name);initialize_queue(queue);pid_t pid = fork();if (pid == 0) {// 子进程 (消费者)consumer(queue);} else {// 父进程 (生产者)producer(queue);}return 0;
}
Linux 实现
内核态(Ring0)实现
内核态代码需要编写一个内核模块,该模块可以访问和操作共享内存中的队列。这里提供一个简化的示例,展示如何在内核模块中访问共享内存。
1. 定义队列结构
内核态代码的队列结构与用户态代码相同:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/uaccess.h>#define QUEUE_SIZE 1024
#define DATA_SIZE 64typedef struct QueueItem {char data[DATA_SIZE];
} QueueItem;typedef struct SPSCQueue {volatile unsigned int head;volatile unsigned int tail;volatile bool isLocked;QueueItem items[QUEUE_SIZE];
} SPSCQueue;
2. 访问共享内存
static SPSCQueue* queue;static int __init spsc_queue_init(void) {// 假设共享内存已经创建并映射到某个地址// 这里只是一个示例,实际情况下需要从用户态传递共享内存的地址queue = (SPSCQueue*) 0x12345678; // 假地址// 初始化队列(如果需要)queue->head = 0;queue->tail = 0;queue->isLocked = false;memset(queue->items, 0, sizeof(queue->items));printk(KERN_INFO "SPSC Queue module initialized.\n");return 0;
}static void __exit spsc_queue_exit(void) {printk(KERN_INFO "SPSC Queue module exited.\n");
}static int kernel_producer() {while (1) {while (queue->isLocked) {// 自旋等待,直到队列解锁}// 检查队列是否已满if ((queue->tail + 1) % QUEUE_SIZE == queue->head) {printk(KERN_INFO "Queue is full, skipping...\n");continue;}queue->isLocked = true;// 写入数据snprintf(queue->items[queue->tail].data, DATA_SIZE, "Data %u from kernel", queue->tail);queue->tail = (queue->tail + 1) % QUEUE_SIZE;printk(KERN_INFO "Produced: %s\n", queue->items[(queue->tail + QUEUE_SIZE - 1) % QUEUE_SIZE].data);queue->isLocked = false;schedule_timeout_interruptible(msecs_to_jiffies(1000)); // 模拟生产时间}return 0;
}static int kernel_consumer() {while (1) {while (queue->isLocked) {// 自旋等待,直到队列解锁}// 检查队列是否为空if (queue->head == queue->tail) {printk(KERN_INFO "Queue is empty, waiting...\n");continue;}queue->isLocked = true;// 读取数据char data[DATA_SIZE];strncpy(data, queue->items[queue->head].data, DATA_SIZE);queue->head = (queue->head + 1) % QUEUE_SIZE;printk(KERN_INFO "Consumed: %s\n", data);queue->isLocked = false;schedule_timeout_interruptible(msecs_to_jiffies(1000)); // 模拟消费时间}return 0;
}static struct task_struct *producer_task;
static struct task_struct *consumer_task;static int __init spsc_queue_init(void) {producer_task = kthread_create(kernel_producer, NULL, "kernel_producer");if (producer_task) {wake_up_process(producer_task);} else {printk(KERN_ERR "Failed to create producer task.\n");return -EFAULT;}consumer_task = kthread_create(kernel_consumer, NULL, "kernel_consumer");if (consumer_task) {wake_up_process(consumer_task);} else {printk(KERN_ERR "Failed to create consumer task.\n");return -EFAULT;}// 假设共享内存已经创建并映射到某个地址// 这里只是一个示例,实际情况下需要从用户态传递共享内存的地址queue = (SPSCQueue*) 0x12345678; // 假地址// 初始化队列(如果需要)queue->head = 0;queue->tail = 0;queue->isLocked = false;memset(queue->items, 0, sizeof(queue->items));printk(KERN_INFO "SPSC Queue module initialized.\n");return 0;
}static void __exit spsc_queue_exit(void) {kthread_stop(producer_task);kthread_stop(consumer_task);printk(KERN_INFO "SPSC Queue module exited.\n");
}module_init(spsc_queue_init);
module_exit(spsc_queue_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple SPSC queue in kernel space");
总结
- 用户态代码:创建和映射共享内存,实现生产者和消费者的逻辑。
- 内核态代码:编写内核模块,访问和操作共享内存中的队列。
这个示例展示了如何在用户态和内核态之间实现一个简单的SPSC队列。实际应用中,你需要根据具体的需求和环境进行进一步的优化和调试。特别注意内存映射和同步机制的实现,以确保系统的稳定性和性能。
Windows实现
在 Windows 下实现一个单生产者单消费者(SPSC)队列,并将其置于跨进程共享内存中,同时在 Ring3 应用程序(生产者)和 Ring0 驱动程序(消费者)之间进行数据传递是一个复杂任务。由于 Ring3 和 Ring0 之间存在隔离,共享内存和同步机制需要通过特定的 Windows 内核机制来实现。
以下是一个示例实现,展示了如何在 Ring3 应用程序(生产者)和 Ring0 驱动程序(消费者)之间进行数据传递。
1. 共享内存和同步机制的设计
我们使用 Windows 的 CreateFileMapping
和 MapViewOfFile
来创建共享内存,并使用内核事件(Kernel Event)来实现同步。
队列结构
#define BUFFER_SIZE 1024typedef struct {char buffer[BUFFER_SIZE]; // 环形缓冲区volatile size_t head; // 生产者指针volatile size_t tail; // 消费者指针
} SpscQueue;
同步机制
- Ring3 侧:使用 Windows API 创建事件对象。
- Ring0 侧:使用 Windows 内核的
KeInitializeEvent
和KeSetEvent
来处理内核事件。
2. Ring3 应用程序(生产者)
Ring3 应用程序负责生成数据并将其放入共享内存中。
#include <windows.h>
#include <stdio.h>#define BUFFER_SIZE 1024typedef struct {char buffer[BUFFER_SIZE];volatile size_t head;volatile size_t tail;HANDLE hNotEmpty; // 通知消费者的内核事件HANDLE hNotFull; // 通知生产者的内核事件
} SpscQueue;int main() {// 1. 创建共享内存HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, // 使用系统分页文件NULL, // 默认安全性PAGE_READWRITE, // 读写权限0, // 对象大小的高32位sizeof(SpscQueue), // 对象大小的低32位"Global\\SpscQueueMap" // 共享内存名称(Global 名称可跨进程访问));if (hMapFile == NULL) {printf("Failed to create file mapping (%d).\n", GetLastError());return 1;}// 2. 映射共享内存到 Ring3 进程地址空间SpscQueue* queue = (SpscQueue*)MapViewOfFile(hMapFile, // 对象句柄FILE_MAP_ALL_ACCESS, // 读写权限0, // 高32位文件映射对象0, // 低32位文件映射对象sizeof(SpscQueue) // 映射视图的大小);if (queue == NULL) {printf("Failed to map view of file (%d).\n", GetLastError());CloseHandle(hMapFile);return 1;}// 3. 初始化队列queue->head = 0;queue->tail = 0;// 4. 创建事件对象queue->hNotEmpty = CreateEvent(NULL, FALSE, FALSE, "Global\\SpscQueueNotEmpty");queue->hNotFull = CreateEvent(NULL, FALSE, TRUE, "Global\\SpscQueueNotFull");if (queue->hNotEmpty == NULL || queue->hNotFull == NULL) {printf("Failed to create events (%d).\n", GetLastError());UnmapViewOfFile(queue);CloseHandle(hMapFile);return 1;}// 5. 生产数据for (int i = 0; i < 100; ++i) {char item[100];sprintf(item, "Item %d", i);// 等待队列有空位WaitForSingleObject(queue->hNotFull, INFINITE);// 写入数据size_t index = queue->head % BUFFER_SIZE;strcpy(queue->buffer + index, item);queue->head++;// 通知消费者队列不为空SetEvent(queue->hNotEmpty);printf("Produced: %s\n", item);// 模拟生产延迟Sleep(100);}// 6. 清理UnmapViewOfFile(queue);CloseHandle(hMapFile);CloseHandle(queue->hNotEmpty);CloseHandle(queue->hNotFull);return 0;
}
3. Ring0 驱动程序(消费者)
Ring0 驱动程序负责从共享内存中读取数据并处理。
驱动程序代码
#include <ntddk.h>#define BUFFER_SIZE 1024typedef struct {char buffer[BUFFER_SIZE];volatile size_t head;volatile size_t tail;KEVENT notEmpty; // 通知消费者的内核事件KEVENT notFull; // 通知生产者的内核事件
} SpscQueue;// 全局共享内存指针
SpscQueue* queue = NULL;void DriverUnload(PDRIVER_OBJECT DriverObject) {UNREFERENCED_PARAMETER(DriverObject);// 取消映射共享内存MmUnmapLockedPages(queue, PsGetProcessSectionBaseAddress(PsGetCurrentProcess()));DbgPrint("Driver unloaded.\n");
}NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {UNREFERENCED_PARAMETER(RegistryPath);DriverObject->DriverUnload = DriverUnload;// 1. 打开共享内存HANDLE hMapFile;OBJECT_ATTRIBUTES objAttrs;UNICODE_STRING mapName;RtlInitUnicodeString(&mapName, L"\\BaseNamedObjects\\Global\\SpscQueueMap");InitializeObjectAttributes(&objAttrs, &mapName, OBJ_CASE_INSENSITIVE, NULL, NULL);NTSTATUS status = ZwOpenFile(&hMapFile,FILE_GENERIC_READ | FILE_GENERIC_WRITE,&objAttrs,NULL,FILE_SHARE_READ | FILE_SHARE_WRITE,FILE_ATTRIBUTE_NORMAL);if (!NT_SUCCESS(status)) {DbgPrint("Failed to open shared memory (%x).\n", status);return status;}// 2. 映射共享内存到 Ring0 地址空间queue = (SpscQueue*)MmMapLockedPagesSpecifyCache(NULL,KernelMode,MmCached,NULL,FALSE,NormalPagePriority);if (queue == NULL) {DbgPrint("Failed to map shared memory.\n");ZwClose(hMapFile);return STATUS_UNSUCCESSFUL;}// 3. 初始化内核事件KeInitializeEvent(&queue->notEmpty, NotificationEvent, FALSE);KeInitializeEvent(&queue->notFull, NotificationEvent, TRUE);// 4. 消费数据for (int i = 0; i < 100; ++i) {// 等待队列不为空KeWaitForSingleObject(&queue->notEmpty, Executive, KernelMode, FALSE, NULL);// 读取数据size_t index = queue->tail % BUFFER_SIZE;char item[100];RtlCopyMemory(item, queue->buffer + index, BUFFER_SIZE);queue->tail++;// 通知生产者队列有空位KeSetEvent(&queue->notFull, 0, FALSE);DbgPrint("Consumed: %s\n", item);// 模拟消费延迟LARGE_INTEGER interval;interval.QuadPart = -10 * 1000 * 1000; // 100msKeDelayExecutionThread(KernelMode, FALSE, &interval);}return STATUS_SUCCESS;
}
4. 关键点说明
共享内存
- 使用
CreateFileMapping
和MapViewOfFile
在 Ring3 侧创建共享内存。 - 在 Ring0 侧使用
ZwOpenFile
和MmMapLockedPagesSpecifyCache
映射共享内存。
同步机制
- Ring3 侧使用 Windows API 创建事件对象。
- Ring0 侧使用内核事件
KEVENT
和KeWaitForSingleObject
进行同步。
跨 Ring 访问
- 共享内存名称使用
Global\\
前缀,确保跨进程和跨 Ring 访问。 - Ring0 驱动程序通过
ZwOpenFile
打开共享内存对象。
5. 运行步骤
- 编译并加载 Ring0 驱动程序。
- 运行 Ring3 生产者应用程序。
- 驱动程序将从共享内存中消费数据并输出到调试输出。
6. 注意事项
- 权限:确保驱动程序和应用程序具有足够的权限访问共享内存。
- 内存对齐:确保共享内存结构对齐正确,避免访问异常。
- 调试输出:使用 DbgView 查看驱动程序的
DbgPrint
输出。
通过这种方式,可以实现 Ring3 和 Ring0 之间的数据传递。如果需要反向传递数据(Ring0 生产者,Ring3 消费者),可以调整生产者和消费者的逻辑。
相关文章:
C++并发编程之跨应用程序与驱动程序的单生产者单消费者队列
设计一个单生产者单消费者队列(SPSC队列),不使用C STL库或操作系统原子操作函数,并且将其放入跨进程共享内存中以便在Ring3(用户模式)和Ring0(内核模式)之间传递数据,是一…...

PostgreSQL技术内幕22:vacuum full 和 vacuum
文章目录 0.简介1.概念及使用方式2.工作原理2.1 主要功能2.2 清理流程2.3 防止事务id环绕说明 3.使用建议 0.简介 在之前介绍MVCC文章中介绍过常见的MVCC实现的两种方式,一种是将旧数据放到回滚段,一种是直接生成一条新数据(对于删除是不删除…...

【网络】:网络编程套接字
目录 源IP地址和目的IP地址 源MAC地址和目的MAC地址 源端口号和目的端口号 端口号 VS 进程ID TCP协议和UDP协议 网络字节序 字符串IP和整数IP相互转换 查看当前网络的状态 socket编程接口 socket常见API 创建套接字(socket) 绑定端口号&…...

java基础概念55-不可变集合
一、定义 不可变集合:不可以被修改的集合,只能查询。(长度、内容均不可被修改) 二、创建不可变集合 【注意】: 此方法是在JDK9中引入的。 2-1、list不可变集合 示例: import java.util.List;public cla…...
深入理解 C++ 函数重载
在 C 中, 函数重载是一个非常强大的特性, 允许多个函数使用相同的名称, 但具有不同的参数类型. 重载解析决定了在给定的调用中, 编译器应选择哪个版本的重载函数. 本文将深入探讨 C 重载解析的工作原理, 帮助你在实际编程中更好地理解这一机制. 重载(Overload) vs 重写(Overri…...

相机和激光雷达的外参标定 - 无标定板版本
1. 实现的效果 通过本软件实现求解相机和LiDAR的外参,即2个传感器之间的三维平移[x, y, z]和三维旋转[roll, pitch, yaw]。完成标定后,可将点云投影到图像,效果图如下: 本软件的优势:(1)无需特…...
Redis 知识速览
文章目录 1. Redis 简介2. Redis 优缺点3. Redis 高性能4. Redis VM 机制5. Redis 数据类型6. 应用场景7. 持久化8. 过期策略9. 内存相关10. 线程模型11. 事务12. 集群 1. Redis 简介 定义:Redis 是一个用 C 语言编写的高性能非关系型(NoSQL)…...

LeetCode 热题 100_从前序与中序遍历序列构造二叉树(47_105_中等_C++)(二叉树;递归)
LeetCode 热题 100_从前序与中序遍历序列构造二叉树(47_105) 题目描述:输入输出样例:题解:解题思路:思路一(递归): 代码实现代码实现(思路一(递归…...
使用sqlplus的easy connect时如何指定是链接到shared server还是dedicated process
在oracle配置了shared server的情况下 可以使用 :shared来指定链接到shared server也可以默认不指定 不指定的情况下会默认链接到shared server 如果想链接到 dedicated process 则必须显式指定链接到dedicated process server type的类型包括DEDICATED, SHARED, or POOLED. […...

ubuntu22.4 ROS2 安装gazebo(环境变量配置)
ubuntu版本:ubuntu22.4 最近在学习ROS2 视频教程古月居的入门课: 视频教程 文字笔记 问题 在学到关于Gazebo的时候,遇到下面问题: 运行 $ ros2 launch gazebo_ros gazebo.launch.py在这里卡住,不弹出gazebo 解决…...
【机器学习:十四、TensorFlow与PyTorch的对比分析】
1. 发展背景与社区支持 1.1 TensorFlow的背景与发展 TensorFlow是Google于2015年发布的开源深度学习框架,基于其前身DistBelief系统。作为Google大规模深度学习研究成果的延续,TensorFlow从一开始就定位为生产级框架,强调跨平台部署能力和性…...

[C++]类与对象(上)
目录 💕1.C中结构体的优化 💕2.类的定义 💕3.类与结构体的不同点 💕4.访问限定符(public,private,protected) 💕5.类域 💕6.类的实例化 💕7.类的字节大小 💕8.类的字节大小特例…...
大数据技术实训:Zookeeper集群配置
一、本地模式安装部署 1)安装前准备 (1)安装jdk (2)拷贝Zookeeper安装包到Linux系统下 (3)解压到指定目录 tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/ 2)配置修改 &am…...
HTML5 加载动画(Loading Animation)
加载动画(Loading Animation)详解 概述 加载动画是指在数据加载过程中,向用户展示的一种视觉效果,旨在提升用户体验,告知用户系统正在处理请求。它可以减少用户的等待焦虑感,提高界面的互动性。 常见的加…...

C语言进阶-2指针(一)
目录 1. 字符指针1.1 一般用法:字符指针指向单字符1.2 第二种用法,字符串首地址给指针变量1.3 习题,下面代码的输出结果是什么?为什么? 2. 指针数组2.1实例—— 字符指针数组2.2实例——整形指针数组2.3 例子,识别下下…...
【人工智能】用Python进行对象检测:从OpenCV到YOLO的全面指南
对象检测是计算机视觉领域的核心任务之一,广泛应用于视频监控、自动驾驶、智能安防等多个场景。随着深度学习技术的发展,基于传统方法的对象检测逐渐被基于神经网络的先进模型所取代。本文将系统地介绍如何使用Python进行对象检测,重点探讨了…...

《深度剖析算法优化:提升效率与精度的秘诀》
想象一下,你面前有一堆杂乱无章的数据,你需要从中找到特定的信息,或者按照一定的规则对这些数据进行排序。又或者,你要为一个物流公司规划最佳的配送路线,以降低成本和提高效率。这些问题看似复杂,但都可以…...

Mysql--重点篇--索引(索引分类,Hash和B-tree索引,聚簇和非聚簇索引,回表查询,覆盖索引,索引工作原理,索引失效,索引创建原则等)
索引是数据库中用于加速查询操作的重要机制。通过索引,MySQL可以快速定位到满足查询条件的数据行,而不需要扫描整个表。合理的索引设计可以显著提高查询性能,但不合理的索引可能会导致性能下降和磁盘空间浪费。因此,理解索引的工作…...
matlab使用 BP 神经网络进行数据预测的完整流程,包括数据读取、数据预处理等等
%% 初始化程序 warning off % 关闭报警信息 close all % 关闭所有图窗 clear % 清空变量 clc % 清空命令行 setdemorandstream(172) %设置随机种子为1%% 读取数据 data xlsread(Y.xlsx); %% 划分训练集…...

systemd-networkd NetworkManager 介绍
systemd-networkd 和 NetworkManager 的详细介绍 systemd-networkd 和 NetworkManager 都是 Linux 系统中常用的网络管理工具,但它们的设计目标和使用场景不同。以下是它们的详细介绍、功能、使用场景和差异。 1. systemd-networkd systemd-networkd 是一个由 syst…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...