【C/C++】从 DPDK 收包到用户态 TCP/IP 协议栈:一个 ustack 示例拆解

LVGL v8.3 图片显示与解码缓存实战:PNG 上屏、C 数组与文件双方案 LVGL v8.3 图片两种存储方式、PNG/JPG 上屏解码器挂载、LV_IMG_CACHE_DEF_SIZE 缓存调优、手动预解码头像有效性判断,及"缓存被动画清空"真实踩坑。 阅读详情

【C/C++】从 DPDK 收包到用户态 TCP/IP 协议栈:一个 ustack 示例拆解

在这里插入图片描述

1. 先把边界说清楚:DPDK 不是协议栈

Linux 内核网络路径大致是:

电信号 -> 网卡 -> 网卡驱动 -> TCP/IP 协议栈 -> POSIX API -> APP

DPDK 做的事情,是让应用绕过内核协议栈,直接从网卡收发包。它更像“高速收包/发包工具箱”,不是完整 TCP/IP 协议栈。真正的以太网、ARP、IP、UDP、TCP 解析和 socket 语义,仍然要由用户态程序自己实现。

这个 ustack 示例的分层可以理解成:

网卡队列 -> DPDK PMD -> rte_mbuf -> ring -> 协议解析 -> UDP/TCP/KNI -> 应用 API

在这里插入图片描述

2. DPDK 初始化:EAL、mempool、网卡队列

用户态协议栈启动时,第一步不是 socket(),而是初始化 DPDK 的运行环境和 mbuf 内存池。

代码片段来自 ustack-main/tcp.c:

if (rte_eal_init(argc, argv) < 0) {
    rte_exit(EXIT_FAILURE, "Error with EAL init\n");
}

struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", NUM_MBUFS,
    0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL) {
    rte_exit(EXIT_FAILURE, "Could not create mbuf pool\n");
}

这里的 rte_mbuf 可以类比内核协议栈里的 sk_buff:它不是单纯的数据指针,而是“报文数据 + 元信息”的载体。DPDK 收到包后,应用拿到的不是裸字节数组,而是一个个 struct rte_mbuf *。

网卡端口初始化的核心逻辑如下:

static void ng_init_port(struct rte_mempool *mbuf_pool) {
    uint16_t nb_sys_ports = rte_eth_dev_count_avail();
    if (nb_sys_ports == 0) {
        rte_exit(EXIT_FAILURE, "No Supported eth found\n");
    }

    const int num_rx_queues = 1;
    const int num_tx_queues = 1;
    struct rte_eth_conf port_conf = port_conf_default;
    rte_eth_dev_configure(gDpdkPortId, num_rx_queues, num_tx_queues, &port_conf);

    rte_eth_rx_queue_setup(gDpdkPortId, 0, 1024,
        rte_eth_dev_socket_id(gDpdkPortId), NULL, mbuf_pool);

    rte_eth_tx_queue_setup(gDpdkPortId, 0, 1024,
        rte_eth_dev_socket_id(gDpdkPortId), &txq_conf);

    rte_eth_dev_start(gDpdkPortId);
}

这段代码做了三件关键事:

  1. 检查当前系统是否有 DPDK 可用网卡。
  2. 配置 1 个 RX 队列和 1 个 TX 队列。
  3. 把 RX 队列挂到前面创建的 mbuf_pool 上。

如果要把性能扩展到多核,后面通常会改成多 RX/TX 队列,让不同 lcore 轮询不同队列。这也是为什么虚拟机环境里经常建议使用 vmxnet3 这类更适合多队列的虚拟网卡。

3. 主循环:网卡收包进 in ring,out ring 再发回网卡

示例代码没有把所有逻辑都堆在主循环里,而是用两个 ring 做解耦:

  • ring->in:主循环从网卡收到包后塞进去。
  • ring->out:协议栈或应用构造好回包后塞进去。
  • pkt_process:另一个 lcore 从 in ring 拿包,做协议分发。

主循环代码节选:

while (1) {
    struct rte_mbuf *rx[BURST_SIZE];
    unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId, 0, rx, BURST_SIZE);

    if (num_recvd > 0) {
        rte_ring_sp_enqueue_burst(ring->in, (void **)rx, num_recvd, NULL);
    }

    struct rte_mbuf *tx[BURST_SIZE];
    unsigned nb_tx = rte_ring_sc_dequeue_burst(ring->out, (void **)tx, BURST_SIZE, NULL);
    if (nb_tx > 0) {
        rte_eth_tx_burst(gDpdkPortId, 0, tx, nb_tx);

        for (unsigned i = 0; i < nb_tx; i++) {
            rte_pktmbuf_free(tx[i]);
        }
    }
}

这个结构很适合教学:主循环只管高速搬运,协议处理线程只管理解报文。后续要做 DDOS 检测、ACL、防火墙、用户态 TCP,都可以挂在中间的协议处理层。

4. 协议分发:Ethernet -> IPv4 -> UDP/TCP/KNI

pkt_process() 是用户态协议栈真正开始“像协议栈一样工作”的地方。

代码节选:

static int pkt_process(void *arg) {
    struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;
    struct inout_ring *ring = ringInstance();

    while (1) {
        struct rte_mbuf *mbufs[BURST_SIZE];
        unsigned num_recvd = rte_ring_mc_dequeue_burst(
            ring->in, (void **)mbufs, BURST_SIZE, NULL);

        for (unsigned i = 0; i < num_recvd; i++) {
            struct rte_ether_hdr *ehdr =
                rte_pktmbuf_mtod(mbufs[i], struct rte_ether_hdr *);

            if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
                struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(
                    mbufs[i], struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));

                ng_arp_entry_insert(iphdr->src_addr, ehdr->s_addr.addr_bytes);

                if (iphdr->next_proto_id == IPPROTO_UDP) {
                    udp_process(mbufs[i]);
                } else if (iphdr->next_proto_id == IPPROTO_TCP) {
                    ng_tcp_process(mbufs[i]);
                } else {
                    rte_kni_tx_burst(global_kni, mbufs, num_recvd);
                }
            }
        }

        udp_out(mbuf_pool);
        ng_tcp_out(mbuf_pool);
    }
}

这段逻辑很直观:

  1. 先看二层 ether_type,只处理 IPv4。
  2. 取出三层 rte_ipv4_hdr。
  3. 顺手把源 IP 和源 MAC 写入 ARP 表。
  4. 根据 next_proto_id 分发到 UDP 或 TCP。
  5. 不认识的包交给 KNI,让内核继续处理。

KNI 这一层在教学里很有价值:不是所有协议都必须用户态自己写。你可以先实现 UDP/TCP 主路径,不关心的报文交回内核,降低调试难度。

5. UDP 入应用缓冲:从报文变成 recv 能读的数据

UDP 的接收路径体现了“协议栈”和“应用 API”之间的分界。

代码节选:

static int udp_process(struct rte_mbuf *udpmbuf) {
    struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(
        udpmbuf, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
    struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr + 1);

    struct localhost *host = get_hostinfo_fromip_port(
        iphdr->dst_addr, udphdr->dst_port, iphdr->next_proto_id);
    if (host == NULL) {
        rte_pktmbuf_free(udpmbuf);
        return -3;
    }

    struct offload *ol = rte_malloc("offload", sizeof(struct offload), 0);
    ol->dip = iphdr->dst_addr;
    ol->sip = iphdr->src_addr;
    ol->sport = udphdr->src_port;
    ol->dport = udphdr->dst_port;
    ol->protocol = IPPROTO_UDP;
    ol->length = ntohs(udphdr->dgram_len);

    ol->data = rte_malloc("unsigned char*",
        ol->length - sizeof(struct rte_udp_hdr), 0);
    rte_memcpy(ol->data, (unsigned char *)(udphdr + 1),
        ol->length - sizeof(struct rte_udp_hdr));

    rte_ring_mp_enqueue(host->rcvbuf, ol);

    pthread_mutex_lock(&host->mutex);
    pthread_cond_signal(&host->cond);
    pthread_mutex_unlock(&host->mutex);

    rte_pktmbuf_free(udpmbuf);
    return 0;
}

这里发生了一次语义转换:

rte_mbuf 中的完整以太网帧
    -> 提取 IPv4/UDP 头
    -> 找到绑定了目标 IP/端口的 localhost
    -> 拷贝 payload 到 offload
    -> 放入 host->rcvbuf
    -> 唤醒正在 nrecvfrom 等待的应用线程

这就是用户态协议栈实现 recvfrom() 的基础。应用看到的是“某个 socket 收到了数据”,协议栈内部看到的是“某个 UDP 报文被解析并投递到对应接收队列”。

在这里插入图片描述

6. TCP 更复杂:它不只是多了一个头

UDP 只要找到端口,把 payload 投递到接收队列即可。TCP 不一样,TCP 要维护连接状态:

  • LISTEN
  • SYN_RCVD
  • ESTABLISHED
  • CLOSE_WAIT
  • LAST_ACK

示例代码里用 struct ng_tcp_stream 作为 TCB:

struct ng_tcp_stream {
    int fd;

    uint32_t dip;
    uint16_t dport;
    uint16_t sport;
    uint32_t sip;

    uint32_t snd_nxt;
    uint32_t rcv_nxt;

    NG_TCP_STATUS status;

    struct rte_ring *sndbuf;
    struct rte_ring *rcvbuf;

    pthread_cond_t cond;
    pthread_mutex_t mutex;
};

这里最重要的不是字段多少,而是含义:

  • sip/dip/sport/dport 确定一条连接。
  • snd_nxt/rcv_nxt 维护序列号和确认号。
  • status 决定当前收到 SYN、ACK、PSH、FIN 时该怎么处理。
  • sndbuf/rcvbuf 把协议栈线程和应用线程隔离开。

所以实现 TCP 的难点不在“能不能读出 TCP header”,而在状态机、重传、窗口、乱序、拥塞控制等一整套行为。这个示例先实现了教学主干,适合用来理解 TCP 协议栈的骨架。

7. VMware + DPDK 环境几个实战注意点

结合目录里的笔记,实验环境最容易卡在这些地方:

# 查看网卡和队列/中断线索
ip -br link
cat /proc/interrupts | grep ens

# DPDK 19.08.2 常见环境变量
export RTE_SDK=/root/dpdk-stable-19.08.2/
export RTE_TARGET=x86_64-native-linux-gcc

# 编译老版本 DPDK 时,新的 GCC 可能需要
make EXTRA_CFLAGS="-fcommon" -j$(nproc)

虚拟机网络模式建议:

  • 桥接:虚拟机和物理网络同网段,但桥接 WLAN 时二层帧不一定稳定。
  • NAT:适合上网,不一定适合从主机直接构造二层包打进 DPDK。
  • Host-only:主机和 VM 在同一个干净的虚拟二层网络里,配静态 ARP 后更适合先跑通实验。

Windows 主机添加静态 ARP 的例子:

netsh interface ipv4 add neighbors 10 192.168.0.7 00-0C-29-5A-7A-0F

如果使用 VFIO,而 VMware 没暴露 IOMMU,可以临时打开 no-IOMMU 模式做实验:

sudo modprobe vfio
echo 1 | sudo tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
sudo modprobe vfio-pci

这只建议在实验环境里使用。生产环境绕过 IOMMU 的安全隔离是不合适的。

8. 总结

这个 ustack 示例最值得看的地方,是它把用户态协议栈拆成了几层很清楚的结构:

DPDK 收发包
  -> rte_mbuf 承载报文
  -> ring buffer 解耦线程
  -> Ethernet/IP/ARP/UDP/TCP 解析
  -> rcvbuf/sndbuf 承接应用 API
  -> KNI 兜底交回内核

写用户态协议栈时,DPDK 解决的是高速 I/O,真正决定系统行为的是协议栈本身:ARP 表怎么维护,UDP 怎么投递,TCP 状态机怎么推进,应用线程怎么被唤醒。把这些边界分清楚,后面再看 mTCP、F-Stack、VPP、lwIP,就不会只停留在“绕过内核很快”这一句口号上。
学习链接: https://github.com/0voice

SAP PP模块 MRP配置实战:5步完成工厂级物料需求计划激活 本文详细介绍了SAP PP模块中物料需求计划(MRP)的工厂级配置实战,通过5个关键步骤从基础环境准备到最终激活。内容涵盖SPRO后台配置、MRP控制者定义、主数据设置及核心参数调整,帮助顾问快速掌握MRP配置清单的完整流程,确保物料需求计划的高效运行。 阅读详情

相关推荐

数学建模小白必看:层次分析法(AHP)从入门到实战(附Python代码)

本文详细介绍了层次分析法(AHP)在数学建模竞赛中的应用,从构建判断矩阵到一致性检验,再到权重计算和实战案例。通过Python代码实现,帮助小白快速掌握AHP的核心技巧,解决评价类难题,提升建模效率。

web39explorer的博客 918

static 的三种用法:C 语言里一个关键字的三张面孔

本文系统讲解 C 语言中 static 关键字的三种用法:修饰局部变量延长生命周期、修饰全局变量与函数限制作用域到本文件。通过一个包含三个文件的完整工程示例对照演示,并从存储期、作用域与链接性三个属性解释其统一原理,最后补充单元测试框架偏爱 static 函数的工程实践。

大模型全栈工程师 · Thanks_ks 313

别再只当代码阅读器了!手把手教你用Understand的实体定位器和信息浏览器,5分钟精准定位代码逻辑

本文深入解析Understand代码分析工具的高级功能,重点介绍实体定位器和信息浏览器的实战应用。通过5分钟快速定位代码逻辑的技巧,帮助开发者从代码阅读者升级为逻辑侦探,提升大型代码库的分析效率。文章包含过滤搜索、调用链追踪等实用方法,是掌握Understand核心功能的必备指南。

weixin_28745821的博客 292

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 239

Wordpress实战之第二章安装wordpress 软件需要的软件

使用下载并安装phpstudy面版工具 wget -O install.sh?https://notdocker.xp.cn/install.sh?&& sudo bash install.sh 2.安装成功如下 ==============运行状态========================= webpanel stop phpstudy running =================...

安全参透之旅 611

一篇文章带你认识 SpringBoot

文章目录一、原有 Spring 优缺点分析二、SpringBoot 的概述 一、原有 Spring 优缺点分析 (1)Spring 的优点分析 Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的 Java 对象(Plain Old Java Object,POJO)实现了EJB的功能。

南淮北安的博客 766

选型笔记:Type‑C 扩展坞方案评估|DP1.4 转 HDMI2.0 桥接芯片 GSV2201S@ACP

本文记录 GSV2201S 这款 DP1.4a 转 HDMI2.0 桥接芯片评估,梳理视频、HDCP、PD 集成能力,总结 Type‑C 转接 Dongle、扩展坞方案的适配边界,以及差分走线、PD 时序、HDCP 握手等高频调试问题,为 C 口视频转接方案选型提供参考。

ACP广源盛的博客 221

基于Termux的Android平台OpenClaw部署:移动端AI助理实现

本文介绍了在Android设备上通过Termux部署OpenClaw AI助理的方案。Android设备的便携性、Termux提供的完整Linux环境以及多样化的硬件配置使其成为理想的部署平台。文章详细分析了不同级别Android设备的硬件规格(从入门级到旗舰级),并提供了最低/推荐配置要求。部署步骤包括Termux安装初始化、Python环境配置以及性能优化建议。该方案充分利用Android设备的传感器和电池供电优势,为移动办公、学习等场景提供了低成本、高便携性的AI助理解决方案。

博客 3011

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 391

YOLOv5改进 | 多尺度特征提取 | 结合多样分支块及融合的高级设计(CVPR2021)

YOLOv5改进 | 多尺度特征提取 | 结合多样分支块及融合的高级设计(CVPR2021)

qq_44224801的博客 2068

Intellij IDEA 使用教程(一)Jdk安装和配置

一.JDK下载 这里下载的是Java SE 8u181 64位版本。可以根据自己电脑配置来选择不同版本。 官网下载JDK 二.安装JDK 双击运行jdk-8u181-windows-x64.exe安装程序 选择安装目录,然后点击下一步 等待安装 安装JRE,就是java的运行环境,可以安装在java目录下 等待安装 安装完成。点击关闭。 三.java环境变量配置 关于环境变量的配置,...

岁月不饶人,我亦未曾饶过岁月 4053

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 336

YOLOv8改进:全网原创首发 | 新颖的多尺度卷积注意力(MSCA),即插即用,助力小目标检测 | NeurIPS2022

多尺度卷积注意力(MSCA),有效地提取上下文信息;1)作为注意力MSCA使用;

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 3844

【C语言】字符与字符串函数

C语言中字符串为以\0结尾的字符数组,标准库函数基于此约定。本文解析字符串内存布局,详解ctype.h与string.h中字符分类、长度计算、拷贝、比较等函数的使用要点,强调长度判断与越界风险,最后通过命令行待办事项示例整合fgets、sscanf、strncpy、strcmp,展示实际应用中的关键细节。

记录在学习过程中学到的知识 343

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 382

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 304

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 437

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 204

【C++】【STL】string_OJ题

本文总结了9道C++ STL中关于string的OJ题目,按操作类型分为三组:反转与就地修改(如917、541、151)、计数与查找(如387、125、58)、模拟与大数运算(如415、43、8)。核心技巧包括双指针收敛、字符分类处理、进位控制及边界值处理。所有题目使用同一套源码,在标准库std::string与自定义容器MyString间切换仅需宏定义,验证结果一致性。代码通过随机测试用例与Trace函数逐行比对,确保逻辑正确性,强调接口对齐的重要性。

这里是Cocatrice的博客,感谢大家一直以来的支持和鼓励,如果对C/C++方向感兴趣的话,可以通过主页的微信名片加Cocatrice的好友,也可以通过邮箱:Chen31980094@163.com 联系,一同加油! 4371

C 语言 malloc() 详解与实战 Demo

本文详解C语言malloc()函数,涵盖其语法、内存原理及实战应用。通过动态整数、数组、结构体与链表的Demo,演示如何安全申请、使用与释放内存,强调避免内存泄漏与空指针检查的重要性,帮助开发者掌握动态内存管理核心技能。

tangPHP的博客 370

【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针

C++类与对象知识(面向过程和面向对象的初步认识,类的初步认识,类的访问限定符及封装,类的作用域、实例化,类的对象模型,this指针)

2601_96587588的博客 880

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 773

C++09_vector类复习

本文以10个核心问题为主线,系统解析C++ vector的底层机制:从基本概念、初始化方式、遍历方法,到size与capacity的区别、扩容策略(VS 1.5×,g++ 2×)、reserve与resize差异、中间增删的挪动原理、迭代器失效原因,直至二维vector的内存布局(含三指针结构)。通过“先会用、再懂扩容、最后看清对象结构”的递进逻辑,揭示vector作为动态数组的本质——连续内存、自动扩容、随机访问高效,但插入删除代价高,迭代器易失效。全文深入浅出,助读者摆脱“黑盒”认知,真正掌握其内存图景

lemo_n_的博客 337

cargo bench 快速上手

是 Rust 内置的基准测试命令,用于。不过,Rust 标准库的#[bench]属性目前仅在版本可用,因此在稳定版(stable)上,社区标准做法是使用框架。

Doublelv 的博客专栏。 339

C++零基础入门第3周:if/else、switch、for/while/do-while 控制流完全指南(附20段可运行代码+避坑清单)

第3周 · 控制流1. 分支:if/else2. 分支:switch3. 循环:for4. 循环:while / do-while5. 范围 for(C++11)6. 跳转:break/continue/return/goto7. 现代 C++ 分支特性避坑:悬空 else / 浮点比较避坑:忘 break / 作用域避坑:边界 off-by-one避坑:do-while 至少执行一次避坑:遍历时修改容器避坑:goto 破坏结构if constexpr / if consteval本周项目:猜数字游戏 #m

qq_58662768的博客 213

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 390

10-01-互操作-C#与C++边界的内存管理

本文深入剖析C#与C++互操作中的内存管理核心问题,涵盖P/Invoke封送机制、内存所有权边界、GC对托管对象的移动风险及固定(Pinning)的性能代价。强调“谁分配谁释放”原则,推荐使用SafeHandle保障异常安全,避免悬垂指针与内存泄漏。提出复制模式优先、零拷贝仅用于性能关键路径的最佳实践,并警示跨平台差异、多线程安全及调试陷阱。最终倡导建立明确的边界协议,实现性能与安全的平衡。

设计 AI 玩游戏🎮 291
上一篇: 【C/C++】从 setjmp 到 ucontext 再到 hook:C 语言协程是怎么跑起来的?
下一篇: 【C/C++】用户态协议栈如何实现 epoll:红黑树、就绪队列和回调通知
SilentSlot
博客等级 码龄16年 44粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值