用户态网络缓冲区设计
基于数组实现的环形缓冲区:
优点
使用固定大小的连续空间做用户态缓冲区,利用了内存访问的局部性,可以提高缓存命中率,提高程序性能,在处理大量数据时,缓存的利用率对性能有着很大的影响
正是基于性能的考虑,使用数组做用户态缓冲区,同时由于固定的空间大小,在使用数组时需要精妙的存取方式,另外,可以使用stl的vacotr的设计思路,动态增长数组的大小,这里暂不做实现
先总结一下环形缓冲区(ringbuffer)的优点:
-
高效的内存管理: 环形缓冲区是由一块连续的内存区域组成的,这样可以减少内存碎片和内存分配的开销,提高内存管理的效率。
-
预先分配的内存: 因为环形缓冲区的大小是固定的,所以可以在系统启动时或者初始化时预先分配所需的内存,而不需要动态分配内存。这可以避免动态内存分配带来的性能开销和内存碎片问题。
-
简单的索引计算: 由于环形缓冲区的内存布局是连续的,所以索引计算非常简单和高效。相比之下,可变长链表等数据结构可能需要更复杂的指针操作和内存访问。
-
更好的缓存性能: 环形缓冲区的连续内存布局可以提高缓存的命中率,因为它利用了局部性原理,使得相关的数据项在内存中更可能是相邻存放的。
代码实现
环形缓冲区结构体:
typedef struct ringbuffer_s {uint32_t size; // 缓冲区数组的大小uint32_t tail; // 尾部索引,即当前可用的数组位置索引uint32_t head; // 头部索引,当前已使用的空间的起始位置索引uint8_t * buf; // 实际缓冲区数组地址
} buffer_t;
其中 tail和head索引的设计 考虑到需要确定当前数组的空闲位置以及已使用的位置,便于添加新数据和取出数据
创建一个缓冲区:
buffer_t * buffer_new(uint32_t 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;
}
一个缓冲区的初始tail和head索引都是位于数组首部的
一些辅助函数:
static uint32_t
rb_isempty(buffer_t *r) { // 缓冲区是否为空return r->head == r->tail;
}static uint32_t rb_isfull(buffer_t *r) { // 缓冲区是否已满return r->size == (r->tail - r->head);
}static uint32_t rb_len(buffer_t *r) { // 已使用空间return r->tail - r->head;
}static uint32_t rb_remain(buffer_t *r) { // 剩余空间return r->size - r->tail + r->head;
}
向缓冲区内添加数据:
int buffer_add(buffer_t *r, const void *data, uint32_t sz) {if (sz > rb_remain(r)) // 如果剩余空间不足,添加失败 return -1;// 如果tail到数组尾部的空间不足以容纳该数据,分段添加到尾部和头部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 = (r->tail + sz) % r->size; // 更新tail索引,可能移动到数组头部return 0;
}
环形缓冲区的添加操作使用了环绕索引,最大限度地利用有限的数组空间
从缓冲区中取出数据
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 = (r->head + actual_sz) % r->size; // 更新head,可能移动到数组头部return sz;
}
更新head的索引也用到了环绕的方法
删除一段数据:
int buffer_drain(buffer_t *r, uint32_t sz) {if (sz > rb_len(r)) // 最多全部删除sz = rb_len(r);r->head = (r->head + sz) % r->size; // 更新索引,使用环绕的方法return sz;
}
获取当前最大可用空间的长度:
uint8_t *buffer_write_atmost(buffer_t *r) {uint32_t wpos = r->tail;uint32_t rpos = r->head;if (wpos >= rpos) {// Case 1: tail is ahead of or equal to headuint32_t first_chunk = r->size - wpos; // Space from tail to end of bufferuint32_t second_chunk = rpos; // Space from start of buffer to headreturn r->buf + wpos;} else {// Case 2: head is ahead of tailreturn r->buf + wpos;}}
buffer_write_atmost函数逻辑
- 如果
tail在head之前(即tail < head),则从tail到head之间的空间是可写的,大小为head - tail - 1。 - 如果
tail在head之后(即tail >= head),则从tail到缓冲区末尾的空间以及从缓冲区头部到head之间的空间都是可写的,需要分两段来计算最大可写空间,返回first_chunk + second_chunk - 1。
head之前(即tail < head),则从tail到head之间的空间是可写的,大小为head - tail - 1。 - 如果
tail在head之后(即tail >= head),则从tail到缓冲区末尾的空间以及从缓冲区头部到head之间的空间都是可写的,需要分两段来计算最大可写空间,返回first_chunk + second_chunk - 1。
至此,已经实现了环形缓冲区的创建、添加、删除操作
推荐学习 https://xxetb.xetslk.com/s/p5Ibb
相关文章:
用户态网络缓冲区设计
基于数组实现的环形缓冲区: 优点 使用固定大小的连续空间做用户态缓冲区,利用了内存访问的局部性,可以提高缓存命中率,提高程序性能,在处理大量数据时,缓存的利用率对性能有着很大的影响 正是基于性能的…...
Linux运维工程师基础面试题整理(三)
Linux运维工程师基础面试题整理(三) 1. 文件inode号有什么用?2. 文件的权限怎么设置与管理?3. 如何SSH免密配置?4. 如何快速部署一个web服务?5. 如何更新Linux系统内核?6. centos中如何配置本地yum源?7.Linux 防火墙如何简单配置?8. 有哪些工具可以批量管理Linux服务器…...
基于单片机与传感器技术的汽车起动线路设计
摘 要:在以发动机为动力源的汽车中,起动系统承担起使发动机由非工作状态进入工作状态的重要作用,属于发动机的附属系统。在传统汽车起动系统的基础上提出将单片机与传感器技术运用到起动控制线路中,通过传感器采集发动机工作状态信…...
C#如何通过反射获取外部dll的函数
在C#中,你可以使用反射(Reflection)来加载外部的DLL(动态链接库)并获取其中的函数(在C#中通常称为方法)。但是,请注意,反射主要用于访问类型信息,并且对于非托…...
从零开始傅里叶变换
从零开始傅里叶变换 1 Overview2 傅里叶级数2.1 基向量2.2 三角函数系表示 f ( t ) f(t) f(t)2.2.1 三角函数系的正交性2.2.2 三角函数系的系数 2.3 复指数函数系表示 f ( t ) f(t) f(t)2.3.1 复指数函数系的系数2.3.2 复指数函数系的正交性 2.4 傅里叶级数总结 3 傅里叶变换…...
解决1万条数据前端渲染不卡的问题
万级数据前端渲染优化 解决思路requestAnimationFrame完整代码 解决思路 将数据分组,通过定时器或requestAnimationFrame两种方式分组渲染到Dom上 requestAnimationFrame 渲染数据-动画requestAnimationFram方法 使用requestAnimationFrame可以将动画的每一帧绘制…...
如何编写一个API——Python代码示例及拓展
下面是一个必备的API的demo,包括用户认证、数据库交互、错误处理和更复杂的异步任务处理。使用Flask来创建一个RESTful API,涉及用户注册、登录、以及获取用户信息的功能。 示例1:编写API 安装依赖 首先,你需要安装以下库来支持示例的功能: pip install flask flask-c…...
UMPNet: Universal Manipulation Policy Network for Articulated Objects
1. 摘要 UMPNet是一个基于图像的策略网络,能够推理用于操纵铰接物体的闭环动作序列。该策略支持6DoF动作表示和可变长度轨迹。 为处理多种类的物体,该策略从不同的铰接结构中学习,并泛化到未见过的物体或类别上。该策略是以自监督探索的方式…...
高通 Android 12/13冻结屏幕
冻结屏幕很多第一次听到以为是Android一种异常现象,实则不然,就是防止用户在做一些非法操作导致问题防止安全漏洞问题。 1、主要通过用户行为比如禁止下拉状态栏和按键以及onTouch事件拦截等,不知道请看这篇文章(Touch事件传递流…...
C++实现图的存储和遍历
前言 许多新手友友在初学算法和数据结构时,会被图论支配过。我这里整理了一下图论常见的存储和遍历方式,仅供参考。如有问题,欢迎大佬们批评指正。 存储我将提到四种方式:邻接矩阵、vector实现邻接表、数组模拟单链表实现的前向星…...
AI--构建检索增强生成 (RAG) 应用程序
LLM 所实现的最强大的应用之一是复杂的问答 (Q&A) 聊天机器人。这些应用程序可以回答有关特定源信息的问题。这些应用程序使用一种称为检索增强生成 (RAG) 的技术。 典型的 RAG 应用程序有两个主要组件 索引:从源中提取数据并对其进行索引的管道。这通常在线下…...
QT7_视频知识点笔记_4_文件操作,Socket通信:TCP/UDP
1.事件分发器,事件过滤器(重要程度:一般) event函数 2.文件操作(QFile) 实现功能:点击按钮,弹出对话框,并且用文件类读取出内容输出显示在控件上。 #include <QFi…...
智慧社区管理系统:打造便捷、安全、和谐的新型社区生态
项目背景 在信息化、智能化浪潮席卷全球的今天,人们对于生活品质的需求日益提升,期待居住环境能与科技深度融合,实现高效、舒适、安全的生活体验。在此背景下,智慧社区管理系统应运而生,旨在借助现代信息技术手段&…...
CustomTkinter:便捷美化Tkinter的UI界面(附模板)
CustomTkinter是一个基于Tkinter的Python用户界面库。 pip3 install customtkinter它提供了各种UI界面常见的小部件。这些小部件可以像正常的Tkinter小部件一样创建和使用,也可以与正常的Tkinter元素一起使用。 它的优势如下: CustomTkinter的小部件和…...
使用MicroPython和pyboard开发板(15):使用LCD和触摸传感器
使用LCD和触摸传感器 pybaord的pyb对LCD设备也进行了封装,可以使用官方的LCD显示屏。将LCD屏连接到开发板,连接后。 使用LCD 先用REPL来做个实验,在MicroPython提示符中输入以下指令。请确保LCD面板连接到pyboard的方式正确。 >>…...
c++20 std::jthread 源码简单赏析与应用
std::jthread 说明: std::jthread 是 C20 中引入的一个新特性,它是线程库中的一个类,专门用于处理 std::thread 与 std::stop_token 和 std::stop_source 之间的交互,以支持更优雅和安全的线程停止机制。 std::stop_source控制…...
自动化测试里的数据驱动和关键字驱动思路的理解
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 初次接触自动化测试时,对数据驱动和关键字驱动不甚理解,觉得有点故弄玄须…...
【30天精通Prometheus:一站式监控实战指南】第6天:mysqld_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细
亲爱的读者们👋 欢迎加入【30天精通Prometheus】专栏!📚 在这里,我们将探索Prometheus的强大功能,并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。🚀 Prometheus是云原生和DevOps的…...
浅析智能体开发(第二部分):智能体设计模式和软件架构
大语言模型(LLM)驱动的智能体(AI Agent)展现出许多传统软件所不具备的特征。不仅与传统软件的设计理念、方法、工具和技术栈有显著的差异,AI原生(AI Native)的智能体还融入了多种新概念和技术。…...
Unity学习笔记---Transform组件
组件介绍 Transform组件在每个游戏对象中都存在,且只存在一个。该组件保存了游戏对象的位置、平移、旋转、缩放等信息。 组件相关方法 //获取当前游戏对象的Transform组件this.transform; getObject.transform; GetComponent<Transform>();//属性 gameObje…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
