消息队列LiteQueue

文章目录

  • 一、简介
  • 二、设计
    • 2.1 队列结构设计
    • 2.2 队列接口设计
  • 三、实现
    • 3.1 队列锁的实现
    • 3.2 创建队列
    • 3.3 写入队列
    • 3.4 读出数据
    • 3.5 判断队列是否为空
    • 3.6 判断队列是否为满
    • 3.7 清空队列
    • 3.8 删除队列
  • 四、测试
  • 参考

一、简介

  • 收到消息时先把接收到的消息放到队列中。
  • 在任务中从队列获取数据。
  • 如果解析过程中再来一帧数据,这帧数据会先存放在消息队列中。
  • 当队列中的上一帧数据解析完成后,任务会从队列中的下一条数据开始解析处理,以此循环,直到队列中的消息解析处理完毕。

二、设计

2.1 队列结构设计

有头尾指针分别指向写入的位置和读出的位置
需要配置队列中最多能存储几帧数据即几个列表项,每一项有多大的空间去保存接收到的数据
在这里插入图片描述
LiteQueue相当于是头部,后面紧跟着的是数据,而且每一个数据的存储大小都是确定的。
考虑到多线程不能同时读或者写,要互斥访问,因此还需要加一个读写锁

/*
LiteQueue : Structure describing queue parameters.
item_num_x: Number of items.
item_size : The size of each list item set when creating the queue,unit: bytes, used to store data received in the queue.
item_size : The counter of items
*/
typedef struct {    volatile uint8_t  queue_write_lock;volatile uint8_t  queue_read_lock;uint8_t   *head;    uint8_t   *tail;size_t    item_num;    size_t    item_size;size_t    item_count; 
}LiteQueue,   *pLiteQueue;

2.2 队列接口设计

使用队列前必定先要创建队列,并确定创建队列的大小,其次是读写队列的接口,以及判断队列是否为空/满、清空队列、删除队列

LiteQueue *LiteQueue_Create(size_t item_num, size_t item_size);  
LiteQueue_Status Write_To_LiteQueue(LiteQueue *queue, uint8_t *buff);  
LiteQueue_Status Read_From_LiteQueue(LiteQueue *queue, uint8_t *buff);
LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue);
LiteQueue_Status LiteQueue_Clear(LiteQueue *queue);
LiteQueue_Status LiteQueue_Delete(LiteQueue *queue);
LiteQueue_Status isLiteQueue_Full(LiteQueue *queue); 
LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue); 

队列的状态用一个枚举类型实现

typedef enum{    LITE_QUEUE_IDLE = 0,    LITE_QUEUE_BUSY,   LITE_QUEUE_ERR,    LITE_QUEUE_OK,LITE_QUEUE_EMPTY,   LITE_QUEUE_NONEMPTY,LITE_QUEUE_FULL,LITE_QUEUE_NONFULL
}LiteQueue_Status;

三、实现

3.1 队列锁的实现

队列锁使用宏定义的方式实现

typedef enum{    LITE_QUEUE_UNLOCK = 0, LITE_QUEUE_LOCK,
}LiteQueueLock;#define  LITE_QUEUE_WRITE_LOCK(__QUEUE__) do{            \if((__QUEUE__)->queue_write_lock == LITE_QUEUE_LOCK){\return LITE_QUEUE_BUSY;                          \} else {                                             \(__QUEUE__)->queue_write_lock = LITE_QUEUE_LOCK; \}                                                    \
}while(0) #define  LITE_QUEUE_WRITE_UNLOCK(__QUEUE__) do{          \(__QUEUE__)->queue_write_lock = LITE_QUEUE_UNLOCK;   \
}while(0)                                                  #define  LITE_QUEUE_READ_LOCK(__QUEUE__) do{             \if((__QUEUE__)->queue_read_lock == LITE_QUEUE_LOCK){ \return LITE_QUEUE_BUSY;                          \} else {                                             \(__QUEUE__)->queue_read_lock = LITE_QUEUE_LOCK;  \}                                                    \
}while(0)       #define  LITE_QUEUE_READ_UNLOCK(__QUEUE__) do{           \(__QUEUE__)->queue_read_lock = LITE_QUEUE_UNLOCK;    \
}while(0)  

3.2 创建队列

/**
* @ brief : Create message queue.
* @ param : {size_t     } item_num : The number of list items in the queue.{size_t     } item_size: The size of each list item, unit: bytes.
* @ return: {LiteQueue *} queue    : Message queue handle pointer.
* @ note  : Create a queue and initialize the queue items to 0, with the head and tail pointers pointing to the starting position of the list items.
*/
LiteQueue *LiteQueue_Create(size_t item_num, size_t item_size){if((item_num < 1) || (item_size < 1)){return NULL;}LiteQueue *queue = (LiteQueue *)malloc(sizeof(LiteQueue) + item_num * item_size);if( queue == NULL ) {printf("LiteQueue malloc failed.\r\n");return NULL;}memset((uint8_t *)queue, 0, sizeof(LiteQueue) + item_num * item_size);queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));queue->tail = queue->head;queue->item_num = item_num;queue->item_size = item_size;queue->item_count = 0;queue->queue_read_lock = LITE_QUEUE_UNLOCK;queue->queue_write_lock = LITE_QUEUE_UNLOCK;return queue;
}

3.3 写入队列

/**
* @ brief : Write data to the queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.{uint8_t        *} buff : Data to be written to the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Writing data when the queue is full will automatically overwrite the first frame of data.
*/
LiteQueue_Status Write_To_LiteQueue(LiteQueue *queue, uint8_t *buff){if((queue == NULL) || (buff == NULL)){return LITE_QUEUE_ERR;}LITE_QUEUE_WRITE_LOCK(queue);if(isLiteQueue_Full(queue) == LITE_QUEUE_FULL){return LITE_QUEUE_FULL;}memcpy(queue->tail, buff, queue->item_size);if(queue->tail == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size){queue->tail = (uint8_t *)queue + sizeof(LiteQueue);}else{queue->tail += queue->item_size;}queue->item_count += 1;LITE_QUEUE_WRITE_UNLOCK(queue);return LITE_QUEUE_OK;
}

3.4 读出数据

/**
* @ brief : Read data from queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.{uint8_t        *} buff : Data to be read from the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Read data starting from the position of the head pointer and save it to the buff.
*/
LiteQueue_Status Read_From_LiteQueue(LiteQueue *queue, uint8_t *buff){if((queue == NULL) || (buff == NULL) || (isLiteQueue_Empty(queue) == LITE_QUEUE_EMPTY)){return LITE_QUEUE_ERR;}LITE_QUEUE_READ_LOCK(queue);if(isLiteQueue_Empty(queue) == LITE_QUEUE_EMPTY){return LITE_QUEUE_EMPTY;}memcpy(buff, queue->head, queue->item_size);  if(queue->head == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size){queue->head = (uint8_t *)queue + sizeof(LiteQueue);}else{queue->head += queue->item_size;}queue->item_count -= 1;LITE_QUEUE_READ_UNLOCK(queue);return LITE_QUEUE_OK;  
}

3.5 判断队列是否为空

/**
* @ brief : Determine whether the queue is empty.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,             it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
inline LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue){if(queue == NULL){return LITE_QUEUE_ERR;}if( queue->item_count == 0 ) {return LITE_QUEUE_EMPTY;}else{return LITE_QUEUE_NONEMPTY;} 
}

3.6 判断队列是否为满

/**
* @ brief : Determine whether the queue is full.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,             it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
inline LiteQueue_Status isLiteQueue_Full(LiteQueue *queue){if(queue == NULL){return LITE_QUEUE_ERR;}if( queue->item_count == queue->item_num) {return LITE_QUEUE_FULL;}else{return LITE_QUEUE_NONFULL;} 
}

3.7 清空队列

/**
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Clear(LiteQueue *queue){if(queue == NULL) {return LITE_QUEUE_ERR;}LITE_QUEUE_WRITE_LOCK(queue);queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));queue->tail = queue->head;queue->item_count = 0;memset(queue->head, 0, queue->item_num * queue->item_size);LITE_QUEUE_WRITE_UNLOCK(queue);return LITE_QUEUE_OK;
}

3.8 删除队列

/**
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Delete(LiteQueue *queue){if(queue == NULL) {return LITE_QUEUE_ERR;}//memset((uint8_t *)queue, 0, sizeof(LiteQueue) + queue->item_num * queue->item_size);free(queue);queue = NULL;return LITE_QUEUE_OK;
}

四、测试

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef unsigned char uint8_t;
typedef unsigned int  uint32_t;/*
LiteQueue : Structure describing queue parameters.
item_num_x: Number of items.
item_size : The size of each list item set when creating the queue,unit: bytes, used to store data received in the queue.
*/
typedef struct {    volatile uint8_t  queue_write_lock;volatile uint8_t  queue_read_lock;uint8_t   *head;    uint8_t   *tail;size_t    item_num;    size_t    item_size;size_t    item_count; 
}LiteQueue,   *pLiteQueue;typedef enum{    LITE_QUEUE_IDLE = 0,    LITE_QUEUE_BUSY,   LITE_QUEUE_ERR,    LITE_QUEUE_OK,LITE_QUEUE_EMPTY,   LITE_QUEUE_NONEMPTY,LITE_QUEUE_FULL,LITE_QUEUE_NONFULL
}LiteQueue_Status;typedef enum{    LITE_QUEUE_UNLOCK = 0, LITE_QUEUE_LOCK,
}LiteQueueLock;#define  LITE_QUEUE_WRITE_LOCK(__QUEUE__) do{            \if((__QUEUE__)->queue_write_lock == LITE_QUEUE_LOCK){\return LITE_QUEUE_BUSY;                          \} else {                                             \(__QUEUE__)->queue_write_lock = LITE_QUEUE_LOCK; \}                                                    \
}while(0) #define  LITE_QUEUE_WRITE_UNLOCK(__QUEUE__) do{          \(__QUEUE__)->queue_write_lock = LITE_QUEUE_UNLOCK;   \
}while(0)                                                  #define  LITE_QUEUE_READ_LOCK(__QUEUE__) do{             \if((__QUEUE__)->queue_read_lock == LITE_QUEUE_LOCK){ \return LITE_QUEUE_BUSY;                          \} else {                                             \(__QUEUE__)->queue_read_lock = LITE_QUEUE_LOCK;  \}                                                    \
}while(0)       #define  LITE_QUEUE_READ_UNLOCK(__QUEUE__) do{           \(__QUEUE__)->queue_read_lock = LITE_QUEUE_UNLOCK;    \
}while(0)   LiteQueue *LiteQueue_Create(size_t item_num, size_t item_size);  
LiteQueue_Status Write_To_LiteQueue(LiteQueue *queue, uint8_t *buff);  
LiteQueue_Status Read_From_LiteQueue(LiteQueue *queue, uint8_t *buff);
LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue);
LiteQueue_Status LiteQueue_Clear(LiteQueue *queue);
LiteQueue_Status LiteQueue_Delete(LiteQueue *queue);
LiteQueue_Status isLiteQueue_Full(LiteQueue *queue); 
LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue); /**
* @ brief : Create message queue.
* @ param : {size_t     } item_num : The number of list items in the queue.{size_t     } item_size: The size of each list item, unit: bytes.
* @ return: {LiteQueue *} queue    : Message queue handle pointer.
* @ note  : Create a queue and initialize the queue items to 0, with the head and tail pointers pointing to the starting position of the list items.
*/
LiteQueue *LiteQueue_Create(size_t item_num, size_t item_size){if((item_num < 1) || (item_size < 1)){return NULL;}LiteQueue *queue = (LiteQueue *)malloc(sizeof(LiteQueue) + item_num * item_size);if( queue == NULL ) {printf("LiteQueue malloc failed.\r\n");return NULL;}memset((uint8_t *)queue, 0, sizeof(LiteQueue) + item_num * item_size);queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));queue->tail = queue->head;queue->item_num = item_num;queue->item_size = item_size;queue->item_count = 0;queue->queue_read_lock = LITE_QUEUE_UNLOCK;queue->queue_write_lock = LITE_QUEUE_UNLOCK;return queue;
}/**
* @ brief : Write data to the queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.{uint8_t        *} buff : Data to be written to the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Writing data when the queue is full will automatically overwrite the first frame of data.
*/
LiteQueue_Status Write_To_LiteQueue(LiteQueue *queue, uint8_t *buff){if((queue == NULL) || (buff == NULL)){return LITE_QUEUE_ERR;}LITE_QUEUE_WRITE_LOCK(queue);if(isLiteQueue_Full(queue) == LITE_QUEUE_FULL){return LITE_QUEUE_FULL;}memcpy(queue->tail, buff, queue->item_size);if(queue->tail == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size){queue->tail = (uint8_t *)queue + sizeof(LiteQueue);}else{queue->tail += queue->item_size;}queue->item_count += 1;LITE_QUEUE_WRITE_UNLOCK(queue);return LITE_QUEUE_OK;
}/**
* @ brief : Read data from queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.{uint8_t        *} buff : Data to be read from the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Read data starting from the position of the head pointer and save it to the buff.
*/
LiteQueue_Status Read_From_LiteQueue(LiteQueue *queue, uint8_t *buff){if((queue == NULL) || (buff == NULL) || (isLiteQueue_Empty(queue) == LITE_QUEUE_EMPTY)){return LITE_QUEUE_ERR;}LITE_QUEUE_READ_LOCK(queue);if(isLiteQueue_Empty(queue) == LITE_QUEUE_EMPTY){return LITE_QUEUE_EMPTY;}memcpy(buff, queue->head, queue->item_size);  if(queue->head == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size){queue->head = (uint8_t *)queue + sizeof(LiteQueue);}else{queue->head += queue->item_size;}queue->item_count -= 1;LITE_QUEUE_READ_UNLOCK(queue);return LITE_QUEUE_OK;  
}/**
* @ brief : Determine whether the queue is empty.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,             it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
inline LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue){if(queue == NULL){return LITE_QUEUE_ERR;}if( queue->item_count == 0 ) {return LITE_QUEUE_EMPTY;}else{return LITE_QUEUE_NONEMPTY;} 
}/**
* @ brief : Determine whether the queue is full.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,             it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
inline LiteQueue_Status isLiteQueue_Full(LiteQueue *queue){if(queue == NULL){return LITE_QUEUE_ERR;}if( queue->item_count == queue->item_num) {return LITE_QUEUE_FULL;}else{return LITE_QUEUE_NONFULL;} 
}/**
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Clear(LiteQueue *queue){if(queue == NULL) {return LITE_QUEUE_ERR;}LITE_QUEUE_WRITE_LOCK(queue);queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));queue->tail = queue->head;queue->item_count = 0;memset(queue->head, 0, queue->item_num * queue->item_size);LITE_QUEUE_WRITE_UNLOCK(queue);return LITE_QUEUE_OK;
}/**
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same,it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Delete(LiteQueue *queue){if(queue == NULL) {return LITE_QUEUE_ERR;}//memset((uint8_t *)queue, 0, sizeof(LiteQueue) + queue->item_num * queue->item_size);free(queue);queue = NULL;return LITE_QUEUE_OK;
}/**
* @ brief : Print the contents of each list item in the queue.
* @ param : {LiteQueue *} queue: Message queue handle pointer.
* @ return: None.
*/
static void PrintLiteQueue(LiteQueue *queue){if(queue == NULL){return ;}for(int i = 0; i < queue->item_num; i++){printf("[item_num:%d] ", i);for(int n = 0; n < queue->item_size; n++){printf("%d ", *((uint8_t *)queue + sizeof(LiteQueue) + i * queue->item_size + n));}printf("\r\n");}
}/**
* @ brief : Print the data in buff.
* @ param : {LiteQueue *} queue: Message queue handle pointer.
* @ return: None.
* @ note  : Used to observe buff data changes and test to verify the correctness of written or read data.
*/
static void PrintBuff(uint8_t *buff, size_t len){if((buff == NULL) || (len < 1)){return ;}printf("Read buff<<<:");for(size_t i = 0; i < len; i++){printf("%d ", buff[i]);}printf("\r\n\r\n");
}int main(){uint8_t writebuff[10] = {0};uint8_t readbuff[10]  = {0};/* Create message queue, 4 list items, each list item has 10 bytes of memory space */pLiteQueue msgQueue = LiteQueue_Create(4, 10);PrintLiteQueue(msgQueue);printf("\r\n");/* Simulate writing and reading to the queue 6 times, and observe the data in the queue by printing */for(int i=0;i<6;i++ ) {/* Simulate data, change the writebuff data and write it to the queue */for(int n = 0; n < msgQueue->item_size; n++){writebuff[n] = (i * msgQueue->item_size + n) % 256;}/* Data is written to the queue */Write_To_LiteQueue(msgQueue, writebuff);PrintLiteQueue(msgQueue);/* Read data from queue */Read_From_LiteQueue(msgQueue, readbuff);PrintBuff(readbuff, sizeof(readbuff));}return 0;
}

在这里插入图片描述

参考

https://mp.weixin.qq.com/s/vI3g4JmSXMyKrpnIV1vjbg

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

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

相关文章

31K star!替换Postman ,开源优雅的API工具:Insomnia

API 调试工具大家第一个想到的肯定是Postman&#xff0c;但是这几年Postman在国内越来越难用&#xff0c;很多人也都在考虑找一个替代品。 今天我们来推荐一个可以替换掉Postman的 API 工具&#xff0c;他开源、支持本地使用&#xff0c;也更加轻量、更加优雅&#xff0c; 目前…

ROS TF坐标变换 - TF树

目录 一、TF树介绍二、TF2与TF三、构建TF树四、rviz查看TF坐标关系 一、TF树介绍 在机器人系统中&#xff0c;存在运动学模型和动力学模型。对于刚体机器人&#xff0c;动力学模型基于刚体动力学&#xff0c;代表机器人系统在运动过程中力/力矩与其运动状态的变化关系。而运动…

Python 基础语法01

变量声明 #运算 num 1 num 1 print("num 1",num)num - 1 print("num - 1", num)num * 4 print("num * 4",num)num 3 num % 2 print("num%2",num)num ** 2 print("num ** 2", num)num 9 num // 2 print("num // …

odoo17 | 创建一个新应用程序

前言 本章的目的是为创建一个全新的Odoo模块奠定基础。 我们将从头开始&#xff0c;以使我们的模块被Odoo识别所需的最低限度。 在接下来的章节中&#xff0c;我们将逐步添加功能以构建一个真实的业务案例。 教程 假设我门需要在odoo上开发一个新app模块例如房地产广告模块。…

C++的面向对象学习(9):文件操作

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、类的封装的多文件实现回顾二、文件操作1.对文件进行操作需要头文件<fstream>2.操作文件的三大类方法&#xff1a;读、写、读写 三、实现文本文件的读、写…

提取 PE 文件的各种信息

前段时间项目需要实现对 Windows PE 文件版本信息的提取&#xff0c;如文件说明、文件版本、产品名称、版权、原始文件名等信息。获取这些信息在 Windows 下当然有一系列的 API 函数供调用&#xff0c;简单方便。 我们先看一下PE文件结构&#xff0c;PE文件由DOS首部&#xff0…

数据结构期末复习(3)栈和队列

堆栈&#xff08;stack&#xff09; 堆栈&#xff08;stack&#xff09;是一种基于后进先出&#xff08;LIFO&#xff0c;Last In First Out&#xff09;原则的数据结构。它模拟了现实生活中的堆栈&#xff0c;类似于一摞盘子或一堆书。 堆栈有两个基本操作&#xff1a;入栈&a…

【代码解析】代码解析之生成token(1)

本篇文章主要解析上一篇&#xff1a;代码解析之登录&#xff08;1&#xff09;里的第8行代码调用 TokenUtils 类里的genToken 方法 https://blog.csdn.net/m0_67930426/article/details/135327553?spm1001.2014.3001.5501 genToken方法代码如下&#xff1a; public static S…

【UE 截图】 自定义截图路径 文件名

目录 0 引言1 实践 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xff1a;【UE 截图】 自定义截图路径 文件名❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01;&#x1f388; 最…

LanceDB:在对抗数据复杂性战役中,您可信赖的坐骑

LanceDB 建立在 Lance&#xff08;一种开源列式数据格式&#xff09;之上&#xff0c;具有一些有趣的功能&#xff0c;使其对 AI/ML 具有吸引力。例如&#xff0c;LanceDB 支持显式和隐式矢量化&#xff0c;能够处理各种数据类型。LanceDB 与 PyTorch 和 TensorFlow 等领先的 M…

漏洞复现-海康威视网络对讲广播系统远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

Linux:apache优化(7)—— 访问控制

作用&#xff1a;为apache服务提供的页面设置客户端访问权限&#xff0c;为某个组或者某个用户加密访问&#xff1b; /usr/local/httpd/bin/htpasswd -c /usr/local/httpd/conf/htpasswd tarro1 #添加admin用户&#xff0c;可以在两个路径中间添加-c是新建文件删除原文件&#…