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

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. 生产者逻辑

生产者负责将数据放入队列中。它应该遵循以下逻辑:

  1. 检查队列是否已满。
  2. 如果队列未满且未被锁定,则锁定队列,并添加数据到队列尾部。
  3. 更新尾部指针,并解锁队列。

5. 消费者逻辑

消费者负责从队列中取出数据。它应该遵循以下逻辑:

  1. 检查队列是否为空。
  2. 如果队列非空且未被锁定,则锁定队列,并从队列头部取出数据。
  3. 更新头部指针,并解锁队列。

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. 运行步骤

  1. 编译并加载 Ring0 驱动程序。
  2. 运行 Ring3 生产者应用程序。
  3. 驱动程序将从共享内存中消费数据并输出到调试输出。

6. 注意事项

  1. 权限:确保驱动程序和应用程序具有足够的权限访问共享内存。
  2. 内存对齐:确保共享内存结构对齐正确,避免访问异常。
  3. 调试输出:使用 DbgView 查看驱动程序的 DbgPrint 输出。

通过这种方式,可以实现 Ring3 和 Ring0 之间的数据传递。如果需要反向传递数据(Ring0 生产者,Ring3 消费者),可以调整生产者和消费者的逻辑。

相关文章:

C++并发编程之跨应用程序与驱动程序的单生产者单消费者队列

设计一个单生产者单消费者队列&#xff08;SPSC队列&#xff09;&#xff0c;不使用C STL库或操作系统原子操作函数&#xff0c;并且将其放入跨进程共享内存中以便在Ring3&#xff08;用户模式&#xff09;和Ring0&#xff08;内核模式&#xff09;之间传递数据&#xff0c;是一…...

22、PyTorch nn.Conv2d卷积网络使用教程

文章目录 1. 卷积2. python 代码3. notes 1. 卷积 输入A张量为&#xff1a; A [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ] \begin{equation} A\begin{bmatrix} 0&1&2&3\\\\ 4&5&6&7\\\\ 8&9&10&11\\\\ 12&13&14&15 \end{b…...

智汇云舟参编《城市轨道交通安全防范系统技术要求》国标正式发布

近日&#xff0c;根据国家标准化管理委员会官网&#xff0c;全国标准信息公共服务平台发布的公告&#xff0c;国家标准《城市轨道交通安全防范系统技术要求》&#xff08;GB/T 26718-2024&#xff09;已由全国城市轨道交通标准化技术委员会上报国家标准化管理委员会&#xff0c…...

C# 数据拟合教程:使用 Math.NET Numerics 的简单实现

C# 数据拟合实战&#xff1a;使用 Math.NET Numerics 快速实现 引言 在科学计算、工程建模或数据分析中&#xff0c;数据拟合是一个非常重要的技术。无论是线性拟合还是非线性拟合&#xff0c;借助适当的工具都可以快速解决问题。本文将向您展示如何使用 C# 和强大的数值计算…...

WEB攻防-通用漏洞_XSS跨站_权限维持_捆绑钓鱼_浏览器漏洞

目录 XSS的分类 XSS跨站-后台植入Cookie&表单劫持 【例1】&#xff1a;利用beef或xss平台实时监控Cookie等凭据实现权限维持 【例2】&#xff1a;XSS-Flash钓鱼配合MSF捆绑上线 【例3】&#xff1a;XSS-浏览器网马配合MSF访问上线 XSS的分类 反射型&#xff08;非持久…...

瑞芯微 RK 系列 RK3588 使用 ffmpeg-rockchip 实现 MPP 视频硬件编解码-代码版

前言 在上一篇文章中&#xff0c;我们讲解了如何使用 ffmpeg-rockchip 通过命令来实现 MPP 视频硬件编解码和 RGA 硬件图形加速&#xff0c;在这篇文章&#xff0c;我将讲解如何使用 ffmpeg-rockchip 用户空间库&#xff08;代码&#xff09;实现 MPP 硬件编解码。 本文不仅适…...

MySQL数据库(SQL分类)

SQL分类 分类全称解释DDLData Definition Language数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;DMLData Manipulation Language数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQLData Query Languag…...

SpringBoot-Day1

1.Springboot入门 创建Maven工程 导入spring-boot-stater-web起步依赖 编写Controller 提供启动类 2.yml配置信息书写与获取 书写 # 发件人信息 email:user: 172349823457qq.comcode: sajdajlwhjfgfkllwhost: smtp.qq.comauth: true ​ # 学生爱好 hobbies:- 打篮球- 踢…...

【JavaScript】基础内容,HTML如何引用JavaScript, JS 常用的数据类型

HTML 嵌入 Javascript 的方式 引入外部 js 文件 <head> <script Language "javaScript" src"index.js"/> </head>内部声明 <head> <script language"javascript">function hello(){alert("hello word&qu…...

mysql中创建计算字段

目录 1、计算字段 2、拼接字段 3、去除空格和使用别名 &#xff08;1&#xff09;去除空格 &#xff08;2&#xff09;使用别名&#xff1a;AS 4、执行算术计算 5、小结 博主用的是mysql8 DBMS&#xff0c;附上示例资料&#xff1a; 百度网盘链接: https://pan.baidu.co…...

如何优化zibll子比主题SEO并设置有效robots文件

如果你不知道 robots.txt 是什么&#xff0c;也不确定如何正确地设置它&#xff0c;本篇文章会向您介绍适用于子比主题的 robots.txt 文件&#xff0c;利用它可以提升 SEO 的效果&#xff0c;避免收录无关页面&#xff0c;从而解决网站被收录但无权重的困境。 作为一款高效的 …...

线程间通信

线程间通信&#xff08;Inter-Thread Communication, 简称ITC&#xff09;是指在多线程编程中&#xff0c;不同线程之间如何交换信息或协调彼此的行为。良好的线程间通信机制是构建高效、可靠的并发程序的关键。Java语言提供了多种内置工具和库来支持线程间的通信&#xff0c;包…...

【实践】操作系统智能助手OS Copilot新功能测评

一、引言 数字化加速发展&#xff0c;尤其人工智能的发展速度越来越快。操作系统智能助手成为提升用户体验与操作效率的关键因素。OS Copilot借助语言模型&#xff0c;人工智能等&#xff0c;对操作系统的自然语言交互操作 推出很多功能&#xff0c;值得开发&#xff0c;尤其运…...

今年的电商年货节,主流的营销策略是怎样?

随着一年的年关将近&#xff0c;新一年的CNY营销也逐渐拉开帷幕。考虑到此时消费需求的膨胀&#xff0c;这个时间不论对于线上还是线下市场而言&#xff0c;都是重要的营销节点。今年CNY营销&#xff0c;电商平台上的主流营销策略是这样&#xff1f;就让我们来简单了解下概况。…...

Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)

文章目录 单例模式1️⃣特性&#x1f4aa;单例模式的类型与实现&#xff1a;类型懒汉式实现(线程不安全)懒汉式实现(线程安全&#xff09;双重锁校验懒汉式(线程安全)饿汉式实现(线程安全)使用类的内部类实现⭐枚举方式实现单例&#xff08;推荐&#xff09;&#x1f44d; 单例…...

基于 Python 的学生成绩管理系统设计与实现

标题:基于 Python 的学生成绩管理系统设计与实现 内容:1.摘要 摘要&#xff1a;本文介绍了一个基于 Python 的学生成绩管理系统的设计与实现。该系统旨在提高学生成绩管理的效率和准确性&#xff0c;方便教师和学生进行成绩查询和分析。本文详细描述了系统的设计思路、功能模块…...

局域网共享文件夹实现两台Windows电脑之间传输文件

文章目录 1. 启用网络发现和文件共享2. 设置共享文件夹3. 记录主电脑的IP地址4. 在第二台电脑访问共享文件夹5. 故障排查6. 启用文件共享未生效方案1&#xff1a;检查服务状态方案2&#xff1a;检查防火墙设置方案3&#xff1a;检查网络类型方案4&#xff1a;使用“管理员命令提…...

JS的事件循环机制

<script>setTimeout(()>{console.log(1)},1000)setTimeout(()>{console.log(2)},3000)var start Date.now();while ( (Date.now() - start) < 10000 ){}console.log(3)</script>执行如下代码会发现先打印3&#xff0c;再打印1 再打印2。 现象本质还是因为…...

《自动驾驶与机器人中的SLAM技术》ch9:自动驾驶车辆的离线地图构建

目录 1 点云建图的流程 2 前端实现 2.1 前端流程 2.2 前端结果 3 后端位姿图优化与异常值剔除 3.1 两阶段优化流程 3.2 优化结果 ① 第一阶段优化结果 ② 第二阶段优化结果 4 回环检测 4.1 回环检测流程 ① 遍历第一阶段优化轨迹中的关键帧。 ② 并发计算候选回环对…...

环境搭建——Mysql、Redis、Rocket MQ部署

前言 在搭建分布式系统时&#xff0c;MySQL、Redis 和 RocketMQ 是常用的基础服务。每个服务各自的功能不同&#xff0c;但它们在数据存储、缓存、消息队列等方面不可或缺。如果你是初学者&#xff0c;别担心&#xff0c;本文会一步步详细教你如何在服务器上通过 Docker 部署这…...

Pycharm连接远程解释器

这里写目录标题 0 前言1 给项目添加解释器2 通过SSH连接3 找到远程服务器的torch环境所对应的python路径&#xff0c;并设置同步映射&#xff08;1&#xff09;配置服务器的系统环境&#xff08;2&#xff09;配置服务器的conda环境 4 进入到程序入口&#xff08;main.py&#…...

git的基本操作

创建分支&#xff1a; 1&#xff0c;拉去develop代码&#xff1b; 2&#xff0c;git checkout develop切换到develop&#xff1b; 3&#xff0c;git branch lyb/lyb_develop &#xff1b; 4&#xff0c;git push --set-upstream origin lyb/lyb_develop 切换分支&#xff0c;上…...

Linux软件包管理工具概览

目录 RPM&#xff08;RedHat Package Manager&#xff09; DPKG&#xff08;Debian Packager&#xff09; APT&#xff08;Advanced Package Tool&#xff09; YUM&#xff08;Yellowdog Updater, Modified&#xff09; DNF&#xff08;Dandified YUM&#xff09; 总结 在…...

unity学习16:unity里向量的计算,一些方法等

目录 1 unity里的向量&#xff1a; 2 向量加法 2.1 向量加法的几何意义 2.2向量加法的标量算法 3 向量减法 3.1 向量减法的几何意义 3.2 向量减法的标量算法 4 向量的标量乘法 5 向量之间的乘法要注意是左乘 还是右乘 5.1 注意区别 5.2 向量&#xff0c;矩阵&#x…...

HTML拖拽功能(纯html5+JS实现)

1、HTML拖拽--单元行拖动 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><…...

w158医院资源管理系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...

【计算机网络】lab3 802.11 (无线网络帧)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;计算机网络_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2.…...

linux进程

课本概念&#xff1a;程序的⼀个执行实例&#xff0c;正在执行的程序等内核观点&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。 进程信息被放在一个叫做进程控制块的数据结构中&#xff0c;可以理解为进程属性的集合.课本上称之为PCB&…...

pytest-allure框架简单封装----测试报告

安装allure-commandline---可以支持allure命令 把对应的压缩包解压后,把xxx/bin配置到环境变量的path去 可以输入allure -version检查版本 pip install allure-pytest2.11.1 生成测试报告 import pytest pytest_args ["-s","-v","--capturesys…...

【2025最新计算机毕业设计】基于SpringBoot+Vue奶茶点单系统(高质量源码,提供文档,免费部署到本地)

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…...

HTML中最基本的东西

本文内容的标签&#xff0c;将是看懂HTML的最基本之基本 &#xff0c;是跟您在写文章时候一样内容。一般想掌握极其容易&#xff0c;但是也要懂得如何使用&#xff0c;过目不忘&#xff0c;为手熟尔。才是我们学习的最终目的。其实边看边敲都行&#xff0c;或者是边看边复制粘贴…...

Open FPV VTX开源之ardupilot配置

Open FPV VTX开源之ardupilot配置 1. 源由2. 配置3. 总结4. 参考资料5. 补充5.1 飞控固件版本5.2 配置Ardupilot的BF OSD5.3 OSD偏左问题 1. 源由 飞控嵌入式OSD - ardupilot配置使用ardupliot配套OSD图片。 Choose correct font depending on Flight Controller SW. ──>…...

基于OQuPy的量子编程实例探究:理论、实践与展望

基于OQuPy的量子编程探究:理论、分析与实践 一、引言 1.1 研究背景与意义 近年来,量子计算作为一种革命性的计算范式,在科学界与产业界引发了广泛关注。它依托量子力学原理,运用量子比特(qubit)进行信息处理,与传统计算相比,具备并行处理、指数级加速等显著优势,为解…...

深入理解 ECMAScript 2024 新特性:正则表达式 /v 标志

ECMAScript 2024 &#xff08;ES15&#xff09;标准引入了新的正则表达式标志 /v&#xff0c;这一新增功能不仅优化了多行匹配的处理&#xff0c;还增加了对特殊字符匹配的支持。这一变革对于需要处理复杂文本数据的应用场景尤为重要&#xff0c;比如日志分析、代码审核等。接下…...

iOS 逆向学习 - Inter-Process Communication:进程间通信

iOS 逆向学习 - Inter-Process Communication&#xff1a;进程间通信 一、进程间通信概要二、iOS 进程间通信机制详解1. URL Schemes2. Pasteboard3. App Groups 和 Shared Containers4. XPC Services 三、不同进程间通信机制的差异四、总结 一、进程间通信概要 进程间通信&am…...

Prompt工程框架介绍与场景选择

文章目录 Prompt工程框架介绍1. CREATE框架2. RACE框架3. RISE框架4. ROSES框架5. E.R.A框架6. SAGE框架7. CARE框架8. PEAR框架9. TIER框架10. LEAP框架11. DEEP框架12. WISE框架13. FOCUS框架14. CLEAR框架15. SMART框架16. CLEAR框架17. LEAN框架18. BRIEF框架19. FAST框架2…...

信号量机制之苹果-橘子问题

桌上有一空盘&#xff0c;允许存放一种水果。爸爸可向盘中放苹果&#xff0c;也可向盘中放橘子&#xff0c;儿子专等吃盘中的橘子&#xff0c;女儿专等吃盘中的苹果。规定当盘空时一次只能放一个水果供吃者取用。 要求&#xff1a;请用信号量机制实现爸爸、儿子、女儿三个并发…...

工业路由器和工业交换机,如何打造高效稳定的工业网络?

工业路由器和工业交换机各有千秋&#xff0c;但如何将它们完美结合&#xff0c;构建稳定高效的工业网络&#xff1f;答案就在这里&#xff01; 工业物联网&#xff08;IIoT&#xff09;是高效、稳定的工业网络成为智慧工厂、工业自动化和远程监控等场景的基础支撑。工业路由器…...

【IDEA 2024】学习笔记--文件选项卡

在我们项目的开发过程中&#xff0c;由于项目涉及的类过多&#xff0c;以至于我们会打开很多的窗口。使用IDEA默认的配置&#xff0c;个人觉得十分不便。 目录 一、设置多个文件选项卡按照文件字母顺序排列 二、设置多个文件选项卡分行显示 一、设置多个文件选项卡按照文件字…...

LabVIEW光流算法的应用

该VI展示了如何使用NI Vision Development Module中的光流算法来计算图像序列中像素的运动矢量。通过该方法&#xff0c;可以实现目标跟踪、运动检测等功能&#xff0c;适用于视频处理、机器人视觉和监控领域。程序采用模块化设计&#xff0c;包含图像输入、算法处理、结果展示…...

WPF 如何添加系统托盘

1.使用Nuget 添加 handycontrol cs xmlns:hc"https://handyorg.github.io/handycontrol" 2.窗体添加控件cs <hc:NotifyIcon x:Name"NotifyIconContextContent" Text"软件名称" ContextMenu"{StaticResource ContextMenu}" Click&…...

小游戏前端地区获取

目前前端获取除了太平洋&#xff0c;没有其它的了。 //在JS中都是使用的UTF-8&#xff0c;然而requst请求后显示GBK却是乱码&#xff0c;对传入的GBK字符串&#xff0c;要用数据流接收&#xff0c;responseType: "arraybuffer" tt.request({url: "https://whoi…...

美摄科技为企业打造专属PC端视频编辑私有化部署方案

美摄科技&#xff0c;作为视频编辑技术的先行者&#xff0c;凭借其在多媒体处理领域的深厚积累&#xff0c;为企业量身打造了PC端视频编辑私有化部署解决方案&#xff0c;旨在帮助企业构建高效、安全、定制化的视频创作平台&#xff0c;赋能企业内容创新&#xff0c;提升品牌影…...

【0x005B】HCI_Write_Default_Erroneous_Data_Reporting命令详解

目录 一、命令概述 二、命令格式及参数 2.1. HCI_Write_Default_Erroneous_Data_Reporting命令格式 2.2. Erroneous_Data_Reporting 三、生成事件及参数 3.1. HCI_Command_Complete事件 3.2. 状态码(Status) 四、命令执行流程 4.1. 命令发起阶段(主机端) 4.2. 命…...

1月13日学习

[HITCON 2017]SSRFme 直接给了源代码&#xff0c;题目名称还是ssrf&#xff0c;那么该题大概率就是SSRF的漏洞&#xff0c;进行代码审计。 <?php// 检查是否存在 HTTP_X_FORWARDED_FOR 头&#xff0c;如果存在&#xff0c;则将其拆分为数组&#xff0c;并将第一个 IP 地址…...

数据平台浅理解

定义 数据平台架构是指用于收集、存储、处理和分析数据的一系列组件、技术和流程的整体架构设计。它就像是一个复杂的数据生态系统的蓝图&#xff0c;旨在高效地管理数据从产生源头到产生价值的整个生命周期。 主要层次 数据源层 这是数据的起点&#xff0c;包含各种类型的数据…...

高通,联发科(MTK)等手机平台调优汇总

一、常见手机型号介绍&#xff1a; ISP除了用在安防行业&#xff0c;还有手机市场&#xff0c;以及目前新型的A/VR眼睛&#xff0c;机器3D视觉机器人&#xff0c;医疗内窥镜这些行业。 下面是一些最近几年发布的,,,旗舰SOC型号&#xff1a; 1.联发科&#xff1a;天玑92…...

win10 Outlook(new) 企业邮箱登录 登录失败。请在几分钟后重试。

windows系统经常弹出使用Outlook(new&#xff09;&#xff0c;自动切过去。 但是登录企业的内网邮箱&#xff0c;折腾了好几次都使用不了。排查网络等问题&#xff0c;在社区找到了答案。 推出一年多不支持企业账户&#xff0c;所以之前的折腾都是浪费时间。 因为这个答案不太…...

Sentaurus TCAD学习笔记:transform指令

目录 一、transform指令简介二、transform指令的实现1.cut指令2.flip指令3.rotate指令4.stretch指令5.translate指令6.reflect指令 三、transform指令示例 一、transform指令简介 在Sentaurus中&#xff0c;如果需要对器件进行翻转、平移等操作&#xff0c;可以通过transform指…...

SpringBoot+Lombok项目实体属性名xXxx格式,前端接收不到

问题解析 今天发现后端传给前端的实体类中&#xff0c;有属性为xXxxx格式的&#xff0c;前端也使用相同名称接收&#xff0c;结果却不显示值&#xff01;研究了一会发现接口请求回来后&#xff0c;原xXxxx的属性名&#xff0c;会被转为全小写。具体原因为&#xff1a;使用Lombo…...