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

用户态缓存:环形缓冲区(Ring Buffer)

目录

环形缓冲区(Ring Buffer)简介

为什么选择环形缓冲区?

代码解析

1. 头文件与类型定义

1.1 头文件保护符

1.2 包含必要的标准库

1.3 类型定义

2. 环形缓冲区结构体

2.1 结构体成员解释

3. 辅助宏与内联函数

3.1 min 宏

3.2 is_power_of_two 内联函数

3.3 roundup_power_of_two 内联函数

4. 环形缓冲区的基本操作

4.1 创建新缓冲区

4.2 获取缓冲区长度

4.3 释放缓冲区

4.4 添加数据到缓冲区

4.5 从缓冲区移除数据

4.6 清空缓冲区的一部分数据

4.7 在缓冲区中搜索特定字符串

4.8 获取写入缓冲区的可写指针

5. 其他辅助函数

5.1 判断缓冲区是否为空

5.2 判断缓冲区是否已满

5.3 获取缓冲区中剩余的空间

6. 代码中的关键概念与实现

6.1 环形地址计算

6.2 缓冲区大小为2的幂次方

6.3 双指针机制

7. 综合应用

7.1 在用户态缓存区中的应用

7.2 处理生产者与消费者速度不匹配

7.3 结合之前的内容

8. 总结


环形缓冲区(Ring Buffer)简介

环形缓冲区是一种高效的数据结构,广泛应用于生产者-消费者模型中。在网络通信中,尤其是用户态缓存区中,环形缓冲区通过循环使用固定大小的内存区域,减少数据移动和内存管理开销,提升数据传输效率。

为什么选择环形缓冲区?

  • 减少数据移动:数据在缓冲区中循环写入和读取,避免了频繁的数据拷贝操作。
  • 高效缓存管理:适用于高并发场景,能够快速响应数据的读写请求。
  • 简化内存管理:固定大小的缓冲区结构简化了内存分配和释放。

代码解析

让我们逐步解析环形缓冲区代码,理解其各个部分的功能和实现细节。

1. 头文件与类型定义

#ifndef _ringbuffer_h
#define _ringbuffer_h#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <limits.h>  // for uint_max
#include <stdint.h>
#include <unistd.h>typedef struct ringbuffer_s buffer_t;buffer_t * buffer_new(uint32_t sz);uint32_t buffer_len(buffer_t *r);void buffer_free(buffer_t *r);int buffer_add(buffer_t *r, const void *data, uint32_t sz);int buffer_remove(buffer_t *r, void *data, uint32_t sz);int buffer_drain(buffer_t *r, uint32_t sz);int buffer_search(buffer_t *r, const char* sep, const int seplen);uint8_t * buffer_write_atmost(buffer_t *r);#endif
1.1 头文件保护符
#ifndef _ringbuffer_h
#define _ringbuffer_h
...
#endif
  • 作用:防止头文件被多次包含,避免重复定义错误。
1.2 包含必要的标准库
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <limits.h>  // for uint_max
#include <stdint.h>
#include <unistd.h>
  • 标准库:提供了内存管理(mallocfree)、字符串操作(memcpy)、固定宽度整数类型(uint32_tuint8_t)等功能。
1.3 类型定义
typedef struct ringbuffer_s buffer_t;
  • 作用:为 struct ringbuffer_s 定义一个别名 buffer_t,简化后续代码的书写。

2. 环形缓冲区结构体

struct ringbuffer_s {uint32_t size;uint32_t tail;uint32_t head;uint8_t * buf;
};
2.1 结构体成员解释
  • size (uint32_t):缓冲区的总大小,以字节为单位。通常为2的幂次方,便于使用位运算实现环形效果。

  • tail (uint32_t):指向缓冲区的写入位置。每次添加数据时,tail 会递增。

  • head (uint32_t):指向缓冲区的读取位置。每次移除数据时,head 会递增。

  • buf (uint8_t *):指向实际数据存储区的指针。数据以字节形式存储。

3. 辅助宏与内联函数

#define min(lth, rth) ((lth)<(rth)?(lth):(rth))static inline int is_power_of_two(uint32_t num) {if (num < 2) return 0;return (num & (num - 1)) == 0;
}static inline uint32_t roundup_power_of_two(uint32_t num) {if (num == 0) return 2;int i = 0;for (; num != 0; i++)num >>= 1;return 1U << i;
}
3.1 min
#define min(lth, rth) ((lth)<(rth)?(lth):(rth))
  • 作用:返回两个值中的较小者,简化代码中的条件判断。
3.2 is_power_of_two 内联函数
static inline int is_power_of_two(uint32_t num) {if (num < 2) return 0;return (num & (num - 1)) == 0;
}
  • 作用:判断一个数是否为2的幂次方。
  • 原理:2的幂次方数在二进制中只有一个1,其减1后所有位都会变为1,按位与结果为0。
3.3 roundup_power_of_two 内联函数
static inline uint32_t roundup_power_of_two(uint32_t num) {if (num == 0) return 2;int i = 0;for (; num != 0; i++)num >>= 1;return 1U << i;
}
  • 作用:将一个数向上舍入到最近的2的幂次方。
  • 实现:通过不断右移操作,计算出需要的位数,然后使用位移生成对应的2的幂次方数。

4. 环形缓冲区的基本操作

4.1 创建新缓冲区
buffer_t * buffer_new(uint32_t sz) {if (!is_power_of_two(sz)) sz = roundup_power_of_two(sz);buffer_t * buf = (buffer_t *)malloc(sizeof(buffer_t) + sz);if (!buf) {return NULL;}buf->size = sz;buf->head = buf->tail = 0;buf->buf = (uint8_t *)(buf + 1);return buf;
}
  • 功能:分配并初始化一个新的环形缓冲区。
  • 步骤
    1. 检查传入的大小 sz 是否为2的幂次方。如果不是,则向上舍入到最近的2的幂次方数。
    2. 分配内存:sizeof(buffer_t) + szbuffer_t结构体和实际数据存储区 buf 一起分配。
    3. 初始化结构体成员:
      • size:设置为调整后的大小 sz
      • headtail:初始化为0,表示缓冲区为空。
      • buf:指向结构体之后的内存区域,即实际的数据存储区。
4.2 获取缓冲区长度
uint32_t buffer_len(buffer_t *r) {return rb_len(r);
}static uint32_t
rb_len(buffer_t *r) {return r->tail - r->head;
}
  • 功能:返回缓冲区中当前存储的数据长度。
  • 实现:通过 tail - head 计算当前缓冲区中有效数据的字节数。
4.3 释放缓冲区
void buffer_free(buffer_t *r) {free(r);r = NULL;
}
  • 功能:释放之前分配的缓冲区内存。
  • 注意:设置指针为 NULL 只是为了防止悬挂指针,但在函数外部无效。
4.4 添加数据到缓冲区
int buffer_add(buffer_t *r, const void *data, uint32_t sz) {if (sz > rb_remain(r)) {return -1;}uint32_t i;i = min(sz, r->size - (r->tail & (r->size - 1)));memcpy(r->buf + (r->tail & (r->size - 1)), data, i);memcpy(r->buf, data+i, sz-i);r->tail += sz;return 0;
}static uint32_t
rb_remain(buffer_t *r) {return r->size - r->tail + r->head;
}
  • 功能:将数据添加到缓冲区中。
  • 步骤
    1. 检查剩余空间
      • 使用 rb_remain(r) 计算缓冲区中剩余的可用空间。
      • 如果要添加的数据大小 sz 超过剩余空间,返回错误 -1
    2. 计算可写入的字节数 i
      • 使用 min(sz, r->size - (r->tail & (r->size - 1))) 计算可以连续写入的最大字节数,避免跨越缓冲区末尾。
    3. 数据拷贝
      • 第一部分:将前 i 字节的数据拷贝到缓冲区当前位置。
      • 第二部分:如果有剩余的数据(sz - i),将其从缓冲区的起始位置开始拷贝,形成环形。
    4. 更新 tail:将 tail 指针增加 sz,标记新数据的结束位置。
    5. 返回成功:返回 0 表示数据添加成功。
4.5 从缓冲区移除数据
int buffer_remove(buffer_t *r, void *data, uint32_t sz) {assert(!rb_isempty(r));uint32_t i;sz = min(sz, r->tail - r->head);i = min(sz, r->size - (r->head & (r->size - 1)));memcpy(data, r->buf+(r->head & (r->size - 1)), i);memcpy(data+i, r->buf, sz-i);r->head += sz;return sz;
}static uint32_t
rb_isempty(buffer_t *r) {return r->head == r->tail;
}
  • 功能:从缓冲区中移除并读取数据。
  • 步骤
    1. 断言缓冲区不为空:使用 assert(!rb_isempty(r)) 确保缓冲区中有数据可读。
    2. 调整读取大小:将 sz 限制为缓冲区中实际存储的数据量 r->tail - r->head
    3. 计算可连续读取的字节数 i
      • 使用 min(sz, r->size - (r->head & (r->size - 1))) 计算可以连续读取的最大字节数,避免跨越缓冲区末尾。
    4. 数据拷贝
      • 第一部分:将前 i 字节的数据从缓冲区当前位置拷贝到目标缓冲区。
      • 第二部分:如果有剩余的数据(sz - i),将其从缓冲区的起始位置开始拷贝。
    5. 更新 head:将 head 指针增加 sz,标记数据的读取位置。
    6. 返回读取的字节数:返回实际读取的数据大小 sz
4.6 清空缓冲区的一部分数据
int buffer_drain(buffer_t *r, uint32_t sz) {if (sz > rb_len(r))sz = rb_len(r);r->head += sz;return sz;
}
  • 功能:从缓冲区中清除 sz 字节的数据,而不读取到用户空间。
  • 步骤
    1. 调整清除大小:将 sz 限制为缓冲区中实际存储的数据量 rb_len(r)
    2. 更新 head:将 head 指针增加 sz,标记数据的清除位置。
    3. 返回清除的字节数:返回实际清除的数据大小 sz
4.7 在缓冲区中搜索特定字符串
int buffer_search(buffer_t *r, const char* sep, const int seplen) {int i;for (i = 0; i <= rb_len(r)-seplen; i++) {int pos = (r->head + i) & (r->size - 1);if (pos + seplen > r->size) {if (memcmp(r->buf+pos, sep, r->size-pos))return 0;if (memcmp(r->buf, sep+r->size-pos, pos+seplen-r->size) == 0) {return i+seplen;}}if (memcmp(r->buf+pos, sep, seplen) == 0) {return i+seplen;}}return 0;
}
  • 功能:在缓冲区中搜索特定的分隔符 sep,用于界定数据包的边界(如协议解析)。
  • 步骤
    1. 遍历缓冲区:从 head 开始,逐个字节检查是否匹配 sep
    2. 计算当前检查的位置 pos:使用 (r->head + i) & (r->size - 1) 实现环形地址计算。
    3. 处理跨越缓冲区末尾的情况
      • 如果 pos + seplen > r->size,表示分隔符跨越缓冲区末尾,需要分两部分比较。
      • 第一部分:比较从 pos 到缓冲区末尾的部分。
      • 第二部分:比较从缓冲区起始位置到剩余长度的部分。
    4. 匹配成功:如果找到匹配的分隔符,返回分隔符的位置(偏移量 i + seplen)。
    5. 未找到匹配:返回 0,表示未找到。
4.8 获取写入缓冲区的可写指针
uint8_t * buffer_write_atmost(buffer_t *r) {uint32_t rpos = r->head & (r->size - 1);uint32_t wpos = r->tail & (r->size - 1);if (wpos < rpos) {uint8_t* temp = (uint8_t *)malloc(r->size * sizeof(uint8_t));memcpy(temp, r->buf+rpos, r->size - rpos);memcpy(temp+r->size-rpos, r->buf, wpos);free(r->buf);r->buf = temp;return r->buf;}return r->buf + rpos;
}
  • 功能:获取当前缓冲区中可写入数据的位置指针,最多可写入的字节数。
  • 步骤
    1. 计算读取和写入位置
      • rposhead 指针在缓冲区中的当前位置。
      • wpostail 指针在缓冲区中的当前位置。
    2. 判断是否需要重新排列缓冲区
      • 如果 wpos < rpos,表示写入位置已环绕到缓冲区的起始位置,需要将数据重新排列,使得写入位置连续。
      • 重新排列
        • 分配新的缓冲区 temp,大小与原缓冲区相同。
        • 将从 rpos 到缓冲区末尾的数据复制到 temp 的起始位置。
        • 将从缓冲区起始位置到 wpos 的数据复制到 temp 的剩余位置。
        • 释放原缓冲区内存,并将 buf 指针指向新的缓冲区 temp
        • 返回新的缓冲区起始位置。
    3. 直接返回写入位置
      • 如果不需要重新排列,直接返回 buf + rpos,即当前可写入的位置。

5. 其他辅助函数

5.1 判断缓冲区是否为空
static uint32_t
rb_isempty(buffer_t *r) {return r->head == r->tail;
}
  • 功能:检查缓冲区是否为空。
  • 返回值1 表示为空,0 表示不为空。
5.2 判断缓冲区是否已满
static uint32_t
rb_isfull(buffer_t *r) {return r->size == (r->tail - r->head);
}
  • 功能:检查缓冲区是否已满。
  • 返回值1 表示已满,0 表示未满。
5.3 获取缓冲区中剩余的空间
static uint32_t
rb_remain(buffer_t *r) {return r->size - r->tail + r->head;
}
  • 功能:计算缓冲区中剩余的可用空间。
  • 返回值:剩余空间的字节数。

6. 代码中的关键概念与实现

6.1 环形地址计算

在环形缓冲区中,headtail 指针是以字节为单位递增的。当指针超过缓冲区的大小时,通过位运算(& (r->size - 1))将其映射回缓冲区的起始位置,实现环形效果。

(r->tail & (r->size - 1))
  • 条件r->size 通常为2的幂次方,这样 (r->size - 1) 就是一个全1的二进制数,可以用来快速计算模运算。
6.2 缓冲区大小为2的幂次方

为了简化环形地址的计算,缓冲区的大小通常设置为2的幂次方。这不仅提高了效率,还使得位运算成为可能,从而加快了数据的读写操作。

6.3 双指针机制
  • head:指向下一个读取位置。
  • tail:指向下一个写入位置。
  • 优势:通过维护两个指针,可以高效地管理生产者(写入)和消费者(读取)之间的数据流动,避免数据冲突和竞争条件。

7. 综合应用

7.1 在用户态缓存区中的应用

在用户态缓存区中,环形缓冲区用于存储和管理网络数据的读写操作。生产者(如内核协议栈)将数据添加到缓冲区,消费者(如应用程序)从缓冲区读取数据。通过环形缓冲区的高效管理,确保数据传输的流畅性和可靠性。

7.2 处理生产者与消费者速度不匹配

当生产者(如内核协议栈)生成数据的速度快于消费者(应用程序)的处理速度时,缓冲区可以暂存这些数据,避免数据丢失。同样,当消费者处理数据的速度快于生产者生成数据的速度时,缓冲区也能有效地管理数据流动,确保数据的连续性。

7.3 结合之前的内容

用户态缓存:高效数据交互与性能优化icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43925427/article/details/142354725?fromshare=blogdetail&sharetype=blogdetail&sharerId=142354725&sharerefer=PC&sharesource=weixin_43925427&sharefrom=from_link在之前的讲解中,我们提到了读写缓存区在网络通信中的重要性,以及不同的缓冲区设计(固定内存块、环形缓冲区、链式缓冲区)对性能和效率的影响。环形缓冲区通过减少数据移动和优化内存管理,提升了数据传输的效率和系统的整体性能。

8. 总结

通过详细解析这段环形缓冲区的代码,我们深入理解了环形缓冲区的结构和工作原理:

  • 高效的数据管理:通过固定大小的缓冲区和双指针机制,环形缓冲区实现了高效的数据读写操作。
  • 减少数据移动:利用环形地址计算和分段拷贝,避免了大量的数据拷贝和移动操作,提升了性能。
  • 灵活的空间管理:通过动态调整和优化(如 buffer_write_atmost 函数),环形缓冲区能够适应不同的数据量需求,保持高效运行。
  • 可靠的数据传输:在生产者和消费者速度不匹配的情况下,环形缓冲区通过暂存和管理数据,确保数据的完整性和可靠性。

参考:

0voice · GitHub

GitHub - TryTryTL/buffer_design

用户态缓存:高效数据交互与性能优化-CSDN博客

相关文章:

用户态缓存:环形缓冲区(Ring Buffer)

目录 环形缓冲区&#xff08;Ring Buffer&#xff09;简介 为什么选择环形缓冲区&#xff1f; 代码解析 1. 头文件与类型定义 1.1 头文件保护符 1.2 包含必要的标准库 1.3 类型定义 2. 环形缓冲区结构体 2.1 结构体成员解释 3. 辅助宏与内联函数 3.1 min 宏 3.2 is…...

Harmony应用 ArkTs AES 加密方法之GCM对称加密

加解密介绍 在数据存储或传输场景中&#xff0c;可以使用加解密操作用于保证数据的机密性&#xff0c;防止敏感数据泄露。 使用加解密操作中&#xff0c;典型的场景有&#xff1a; 使用对称密钥的加解密操作。 使用非对称密钥的加解密操作。 使用RSA&#xff08;PKCS1_OAEP…...

热成像在战场上的具体作用!!!

1. 探测和跟踪敌人 原理&#xff1a;人体和许多类型的军事设备都会发热&#xff0c;热成像技术通过探测这些红外辐射&#xff0c;能够在远距离探测和跟踪敌人的位置。 应用场景&#xff1a;这一功能在夜间或有覆盖物&#xff08;如草丛、树林&#xff09;的情况下尤为有效&am…...

2024年09月20日《每日一练》

1、 根据我国“十三五”规划纲要&#xff0c;&#xff08;&#xff09;不属于新一代信息技术产业创新发展的重点。 A 人工智能 B 移动智能终端 C 先进传感器 D 4G D P13 此题考察的是新一代信息技术&#xff0c;必须掌握&#xff0c;高频考点 国在“十三五“规划纲要中&#x…...

使用 SSCB 保护现代高压直流系统的优势

在各种应用中&#xff0c;系统效率和功率密度不断提高&#xff0c;这导致了更高的直流系统电压。然而&#xff0c;传统的电路保护解决方案不足以在保持高可靠性和安全性的同时有效保护这些高压配电系统。 固态断路器 &#xff08;SSCB&#xff09; 和电熔断器具有众多优点&…...

Linux基础命令——文件系统的日常管理

目录 一.如何查看当前工作目录?&#xff08;你现在所处的位置路径&#xff09; 二.命令touch的用途是什么?还有别的方法新建文件吗? &#xff08;1&#xff09;创建空文件 &#xff08;2&#xff09;如果已经存在这个文件&#xff0c;就会更新创建时间。 &#xff08;3…...

uniapp使用高德地图设置marker标记点,后续根据接口数据改变某个marker标记点,动态更新

最近写的一个功能属实把我难倒了,刚开始我请求一次数据获取所有标记点,然后设置到地图上,然后后面根据socket传来的数据对这些标记点实时更新,改变标记点的图片或者文字, 1:第一个想法是直接全量替换,事实证明这样不行,会很卡顿,有明显闪烁感,如果标记点比较少,就十几个可以用…...

坦白了,因为这个我直接爱上了 FreeBuds 6i

上个月&#xff0c;华为发布的 FreeBuds 6i 联名了泡泡玛特真的超级惊艳&#xff0c;不少宝子被这款耳机的颜值所吸引&#xff0c;而它的实力更是不容小觑的。FreeBuds 6i 是一款性能强大的降噪耳机&#xff0c;它一直在强调平均降噪深度&#xff0c;但是应该很多人对这个概念很…...

006.MySQL_查询数据

课 程 推 荐我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448;入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448;虚 拟 环 境 搭 建 &#xff1a;&#x1…...

【C#生态园】从图像到视觉:Emgu.CV、AForge.NET、OpenCvSharp 全面解析

C#图像处理库大比拼&#xff1a;功能对比、安装配置、API概览 前言 图像处理和计算机视觉在现代软件开发中扮演着重要角色&#xff0c;而C#作为一种流行的编程语言&#xff0c;拥有许多优秀的图像处理库。本文将介绍几个用于C#的图像处理和计算机视觉库&#xff0c;包括Image…...

1、无线通信的发展概况

无线通信是指双方至少一方使用无线方式进行信息的交换与传输&#xff0c;包括移动体&#xff08;行人、车辆、船舶以及飞机&#xff09;和移动体之间的通信&#xff0c;也包括移动体与固定点&#xff08;固定点的移动电台或有线通信&#xff09;之间的通信。 随着无线通信的范围…...

虚拟机安装xubuntu

新建一个新的虚拟机&#xff0c;选择自定义安装 默认下一步 选择稍后安装操作系统 选择所要创建的系统及版本 填写虚拟机的名称及创建的虚拟机保存的位置 选择处理器和内核的数量 处理器数量指的是&#xff1a;虚拟的CPU数量。 每个处理器的内核数量指的是&#xff1a;虚拟CPU…...

携手鲲鹏,长亮科技加速银行核心系统升级

新经济周期下&#xff0c;银行净息差持续收窄、盈利压力加大、市场竞争日趋加剧。同时&#xff0c;国家相关政策不断出台&#xff0c;对金融科技的自主创新与安全可控提出了更高要求。 在这样的大背景下&#xff0c;银行业的数字化转型已经步入深水区。其中&#xff0c;核心系统…...

新鲜的Win11/10镜像,全系列下载!

下载&#xff1a;新鲜的Win11/10镜像&#xff0c;全系列下载&#xff01; | 瑆箫博客 Windows每个月都来一次例行更新&#xff0c;大吉大利今晚装机&#xff01;2024年9月份ISO镜像&#xff0c;来咯~我们不生产系统&#xff0c;我们只是大自然微软的搬运工本文提供Windows11、…...

iPhone 16系列:摄影艺术的全新演绎,探索影像新境界

在科技的浪潮中&#xff0c;智能手机摄影功能的进化从未停歇。 苹果公司即将推出的iPhone 16系列&#xff0c;以其卓越的相机升级和创新特性&#xff0c;再次站在了手机摄影的前沿。 从硬件到软件&#xff0c;从拍照体验到图像处理&#xff0c;iPhone 16系列都展现了其在移动…...

移动应用开发中的技术选择:优缺点全面解析

在移动应用开发领域&#xff0c;技术的快速演变和多样化使得开发者面临着多种选择。随着市场需求的不断变化&#xff0c;各种框架和工具应运而生。本文将分析当前主流应用开发技术的优势与劣势&#xff0c;帮助开发者选择最适合的技术栈。 一、原生开发 优势 性能卓越&#…...

宿舍管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 宿舍管理系统拥有三个角色&#xff0c;分别为系统管理员、宿舍管理员以及学生。其功能如下&#xff1a; 管理员&#xff1a;宿舍管理员管理、学生管理、宿舍楼管理、缺勤记录管理、个人密…...

原生+jquery写自动消失的提示框

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>自动消失消息提示</title> <style>/…...

Android14 蓝牙 BluetoothService 启动和相关代码介绍

Android14 蓝牙 BluetoothService 启动和相关代码 文章目录 Android14 蓝牙 BluetoothService 启动和相关代码一、前言二、代码分析介绍1、蓝牙 BluetoothService 启动和相关代码&#xff08;1&#xff09;蓝牙服务相关的有几个类有&#xff1a;&#xff08;2&#xff09;几个蓝…...

【秋招笔试-支持在线评测】9.19小米秋招(已改编)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集…...

动态IP与静态IP:哪种更适合用户使用?

在现代网络环境中&#xff0c;IP地址的管理对于网络的运行和安全至关重要。IP地址是网络中每一个设备的唯一标识符&#xff0c;通常分为动态IP和静态IP两种类型。每种类型都有其独特的优缺点&#xff0c;适用于不同的使用场景。本文将探讨动态IP和静态IP的特点&#xff0c;并分…...

Flowable基础篇

Flowable基础篇 课程环境说明&#xff1a; JDK8Flowable6.7.2MySQL8 一、基础知识科普 1.工作流发展 BPM(BusinessProcessManagement)&#xff0c;业务流程管理是一种管理原则&#xff0c;通常也可以代指BPMS(BusinessProcessManagementSuite)&#xff0c;是一个实现整合不同…...

SQL Server数据库简单的事务日志备份恢复

模拟数据库备份恢复过程 1.基础操作 1.创建TestDB数据库&#xff0c;并添加数据 USE [master] GO CREATE DATABASE TestDB CONTAINMENT NONE ON PRIMARY ( NAME NTestDB, FILENAME ND:\TestDB.mdf , SIZE 8192KB , MAXSIZE UNLIMITED, FILEGROWTH 65536KB ) LOG ON ( …...

二级C语言2023-3易错题

1 下列叙述中正确的是 A. 循环队列是队列的链式存储结构 B. 能采用顺序存储的必定是线性结构 C. 所有的线性结构都可以采用顺序存储结构 D. 具有两个以上指针的链表必定是非线性结构 正确答案&#xff1a;C C的陈述是正确的。线性结构是一种基本的数据结构&#xff0c;它包括…...

【2024】前端学习笔记9-内部样式表-外部导入样式表-类选择器

学习笔记 内部样式表外部导入样式表类选择器&#xff1a;class 内部样式表 内部样式表是将 CSS 样式规则写在 HTML 文档内部。通过<style>标签在 HTML 文件的<head>部分定义样式。 简单示例&#xff1a; <!DOCTYPE html><html><head><style…...

回归传统,Domino拷贝式迁移!

大家好&#xff0c;才是真的好。 前面讲太多普及型的概念&#xff0c;今天我们来点实在的内容。 在Notes/Domino的黄金年代&#xff0c;有一件事情大家干得风生水起&#xff0c;那就是Domino服务器迁移。 要么迁移到另一台硬件服务器上&#xff0c;要么迁移到新换的磁盘当中…...

商品搜索API返回值解析:关键字搜索如何优化商品推荐

优化商品推荐 关键字搜索优化&#xff1a;分析用户搜索的关键字&#xff0c;并确定每个关键字的权重&#xff0c;使用这些权重来调整搜索结果和推荐结果的相关性。 taobao.item_search 公共参数 请求地址: 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接…...

暴雨总裁孙辉:不是所有应用都要追求“大”模型

9月19日&#xff0c;在暴雨青海渠道大会上&#xff0c;暴雨总裁孙辉谈及了AI的发展趋势和暴雨的智能化战略。 当前AI依然火热&#xff0c;孙辉认为&#xff1a;“从商业应用角度看&#xff0c;从来没有一项技术进步像AI一样&#xff0c;在如此短的时间内产生如此大的影响。AI技…...

【掌桥科研-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…...

BERT推理显存爆满?7个实用技巧教你快速优化!显存优化的最佳实践指南

如果只使用BERT进行推理得到词向量&#xff0c;但显存仍然爆满&#xff0c;以下几个建议可以帮助缓解显存问题&#xff1a; 分批处理&#xff08;Batching&#xff09;&#xff1a; 即使是在推理阶段&#xff0c;也可以将输入数据分成较小的批次&#xff08;batch&#xff09;&…...