<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Network on Yu's Space</title><link>https://code-agree.github.io/tags/network/</link><description>Recent content in Network on Yu's Space</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 27 Aug 2026 00:10:00 +0800</lastBuildDate><atom:link href="https://code-agree.github.io/tags/network/index.xml" rel="self" type="application/rss+xml"/><item><title>低延迟 Socket 优化:从成本模型到平台层</title><link>https://code-agree.github.io/blog/2026-08-27-low_latency_socket/</link><pubDate>Thu, 27 Aug 2026 00:10:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-27-low_latency_socket/</guid><description>一份自顶向下的分析框架。目标不是罗列参数,而是建立&amp;quot;为什么&amp;quot;的因果链—— 每一项优化都应该能追溯到它消除了哪一类物理成本。
0. 方法论:为什么不能从参数列表开始 #&amp;ldquo;Socket 有哪些优化&amp;quot;是一个被问烂了的问题,而绝大多数答案是失败的,因为它们是平铺的清单: TCP_NODELAY、SO_REUSEPORT、零拷贝、DPDK……
清单式回答有三个致命缺陷:
无法判断适用性。同一个参数在吞吐场景和延迟场景下的取值是相反的,脱离目标谈优化没有意义。 无法排序。不知道哪一项值 10μs、哪一项值 100ns,就会把精力花在错误的地方。 无法发现清单外的问题。真实系统的瓶颈常常不在清单上——它在 BIOS 里、在 NUMA 拓扑里、在 cache 替换策略里。 本文采用的路径是:
解剖数据路径 → 建立成本模型 → 确立度量纪律 → 逐层消除成本 → 验证 (第 1 节) (第 2 节) (第 3 节) (第 4-7 节) (第 8 节) 核心论点:Socket 优化的本质是在从网线到应用逻辑的路径上,系统性地消除排队点、 切换点、拷贝点和失效点。所有具体参数都是这四个原理在不同层次上的投影。
1. 解剖:一个包到底经历了什么 #1.1 接收路径(RX) #① NIC 收包,DMA 写入 RX ring 预挂的 buffer ② NIC 触发 MSI-X 中断(可能被 interrupt coalescing 延迟) ③ 硬中断处理:屏蔽该队列中断,raise NET_RX softirq ④ softirq 上下文 napi_poll:取 descriptor,构造 skb,GRO 聚合 ⑤ netif_receive_skb → ip_rcv → tcp_v4_rcv 查 socket hash → 加 socket 锁 → 序号检查 → 入 receive_queue ⑥ sk_data_ready → wake_up → try_to_wake_up 目标线程若在别的核 → 发 IPI → 对端调度器抢占 ⑦ 用户态 epoll_wait 返回 → recv() → copy_to_user 第一个反直觉的结论:第 ⑤ 步——真正的 TCP 协议处理——在 fast path 上只有几百纳秒。 而整条路径的 wire-to-app 延迟通常是 5–15μs。</description></item><item><title>从 send() 到网线：Linux 网络发包全路径与缓冲区概念辨析</title><link>https://code-agree.github.io/blog/2026-06-26-tcp_send_path/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-06-26-tcp_send_path/</guid><description>本文是《从网线到策略引擎：Linux 网络收包全路径深度解析》的镜像篇。收包讲的是「数据如何从网卡到达用户态」，本文反过来，讲一个本地用户态的数据如何通过 TCP 发送到对端，并顺带把几个最容易混淆的概念——网卡、socket、socket 缓冲区、网卡缓冲区、内核缓冲区——一次性辨析清楚。
〇、先把概念辨析清楚（这是理解全流程的地基） #很多人讲发包讲不清，根子在于把不属于同一层的东西当成一回事。这几个词其实分属三个层次。
1. socket —— 是「对象」，不是「缓冲区」 #socket 是内核里的一个数据结构，代表「一条通信端点」。你 socket() 拿到的 fd，背后就是它。它记着源/目的 IP、端口、协议、连接状态、收发队列指针、TCP 状态机变量等。
socket 是「这条连接的档案 + 控制中心」，本身不存数据，但挂着两个缓冲区。
2. socket 缓冲区 —— per-socket 的软件收发队列 #每个 socket 自带两个队列：
发送缓冲区（send buffer，SO_SNDBUF）：send() 写进来的数据先攒在这。对 TCP，它还兼任重传底本——没收到 ACK 的数据必须留在这里。 接收缓冲区（recv buffer，SO_RCVBUF）：收到的数据攒在这等 recv()。 它在内核内存里，由协议栈管理。
3. 内核缓冲区 —— 一个泛称 #这个词最容易含糊，它不是某个具体东西：
狭义场景下，「内核缓冲区」常就指上面的 socket 缓冲区——相对「用户缓冲区」而言，数据从用户态拷进内核态的那块。 广义上，还包括数据在协议栈里流转时的载体——sk_buff（skb）：内核描述一个网络包的核心结构，封装 TCP/IP/以太网头就是往它上面填。 记法：用户缓冲区 ↔ 内核缓冲区，是「用户态 vs 内核态」这条线；socket 缓冲区是内核缓冲区里最靠近应用的那一段。
4. 网卡（NIC）—— 硬件 #真正把数据变成电信号/光信号送上线的 PCIe 硬件设备。CPU 通过「描述符 + 寄存器（门铃）+ DMA + 中断」这套接口跟它打交道。</description></item><item><title>HFT 系统中的延迟测量与绝对时间戳：一份工程实践指南</title><link>https://code-agree.github.io/blog/2026-04-27-timestamp/</link><pubDate>Mon, 27 Apr 2026 00:42:44 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-27-timestamp/</guid><description>面向 Linux/x86_64 平台,覆盖进程内耗时测量、端到端链路延迟、日志时间戳以及监管合规场景。
0. 为什么 HFT 对&amp;quot;时间&amp;quot;如此苛刻 #在高频交易系统里,&amp;ldquo;时间&amp;quot;其实包含三个不同的工程问题,它们的解法几乎没有交集,初学者最容易混为一谈:
相对延迟(Relative Latency):我执行一段代码/一次订单处理花了多少纳秒?内部哪段代码是瓶颈?这关心单机内的时间差,要求极低的测量开销和极高的精度。 本机间隔与超时(Local Intervals):每 5 分钟跑一次校对、心跳 30 秒一次、订单 100 ms 内未回报视为超时——这关心单机内的时间流逝,要求时钟单调、不被 NTP 跳变干扰。 绝对时间戳与跨机器比较(Absolute Timestamp):某事件发生在 UTC 的哪一刻?接收时刻和交易所发出时刻差多少?这关心与外界对齐的墙上时间,要求与交易所、监管的时间基准一致,MiFID II RTS 25 规定业务时钟偏离 UTC 不得超过 100 μs。 第一类追求极致的低开销(单次测量 &amp;lt;10 ns),第二类追求逻辑正确性(不能被时钟跳变坑),第三类追求极致的对齐精度(与 UTC 偏差 &amp;lt;1 μs)。本文按这三条主线展开。
1. Linux 上的时钟源总览 #把所有候选工具按&amp;quot;离硬件远近&amp;quot;排一次,对应它们的精度、开销和 HFT 可用性:
工具 精度 单次开销 HFT 适用 说明 rdtsc / rdtscp CPU 周期(~0.3 ns) 15~40 cycles(~5~15 ns) ✅ 首选 进程内热路径唯一能做到个位数纳秒开销的手段 clock_gettime(CLOCK_MONOTONIC) (vDSO) 纳秒 ~20~30 ns ✅ 常用 底层也是 TSC + 换算,省去自己标定 clock_gettime(CLOCK_MONOTONIC_RAW) 纳秒 ~20~30 ns ⚠️ 特殊 不受 NTP 频率调整,更&amp;quot;原生&amp;rdquo; clock_gettime(CLOCK_REALTIME) 纳秒 ~20~30 ns ❌ 测延迟 / ✅ 打墙钟 会被 NTP 跳变,不能用于计算耗时 clock_gettime(CLOCK_TAI) 纳秒 ~20~30 ns ✅ 日志 不含闰秒,但仍会跟随系统墙钟 step gettimeofday() 微秒 ~20 ns ❌ 精度不够,POSIX 已不推荐 HPET 10~100 ns 几百 ns ~ 1 μs ❌ MMIO 访问,比 TSC 慢数十倍 NIC 硬件时间戳 数十 ns(含 PHY) 内核旁路返回 ✅ 必备 唯一能测 wire-to-wire 的手段 time() / clock() 秒 / 进程 CPU 时间 — ❌ time() 太粗,clock() 不是墙钟 表中的 clock_gettime 开销默认假设 x86_64 新内核走 vDSO。较老内核上 CLOCK_MONOTONIC_RAW/CLOCK_TAI 可能退化为 syscall,开销会高出数倍。</description></item><item><title>kernel socket vs DPDK：一条 WebSocket 帧的两段旅程与纳秒级实测</title><link>https://code-agree.github.io/blog/2026-04-25-kernel_socket_vs_dpdk/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-25-kernel_socket_vs_dpdk/</guid><description>— 一条 180 字节的 TLS 帧，从网卡到用户回调，在两条路径上分别要走多久？走哪些步骤？为什么差 3-10 倍？
0. TL;DR #本文做三件事：
走一遍 kernel socket 收包路径——9 步，每步做什么，花多少纳秒 走一遍 DPDK + F-Stack + BIO_s_mem 路径——5 步，每步的用户态替代品 **用真实项目数据（FlashShark-ws，AWS EC2 VM，60 s，n=26 835）**对比两条路径的差距，包括一组真 A/B 对照（M0 kernel 9.6 µs vs M7 DPDK 2.8 µs 的 TLS 解密段） 最硬的两个数：
一条 180 B 的 Binance bookTicker 帧在 DPDK 路径上 e2e p50 = 5.3 µs、min = 1.1 µs 同样的 TLS 解密工作，kernel socket + OpenSSL BIO_s_socket 路径 p50 ≈ 9.</description></item><item><title>同一专线、同一秒、延迟不一样——从一次排查看网卡中断与核心隔离的本质</title><link>https://code-agree.github.io/blog/2026-04-09-buffer/</link><pubDate>Thu, 09 Apr 2026 00:03:41 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-09-buffer/</guid><description>本文从一个真实的加密货币交易系统延迟异常出发，逐层拆解网卡中断机制、多队列架构、PPS 瓶颈、Buffer Bloat、网卡隔离与 CPU 核组规划，帮助读者建立从物理网卡到 CPU 处理的完整认知。
一、问题现象 #在一台 AWS c8g.metal-48xl（Graviton4，192 核，裸金属）上，同一条专线接入了多个交易所的行情。某天观察到以下现象：
时间 BN 延迟 GATE 延迟 间隔 17:33:36 248ms 20ms 同秒，相隔 7ms 17:35:46 73-82ms（连续 8 包） 20ms 同秒，相隔 170ms 17:41:17 86ms 20ms 同秒，相隔 142ms 关键矛盾：如果是专线本身的问题（光纤故障、中继设备拥塞），同一时刻所有流量都应该受影响。但 GATE 始终稳定在 20ms，只有 BN 在波动。
结论：问题出在 TY 端到 BN 的独有路径上。 但这次排查引出了一系列关于网卡架构和 CPU 资源分配的深层问题，值得系统梳理。
二、中断到底是什么：一次中断，两个角色 #很多人把&amp;quot;网卡中断&amp;quot;和&amp;quot;CPU 中断&amp;quot;当作两种不同的中断，但实际上它们描述的是同一次中断的发起方和执行方。
打个比方：有人按了你家门铃（网卡发中断），你听到铃声后起身去开门、收快递、拆包裹（CPU 处理中断）。门铃和你的行动不是两件独立的事，而是同一件事的两端。
网卡中断（发起方）：网卡通过 DMA 将数据写入 Ring Buffer 后，通过 PCIe 总线向 CPU 的中断控制器发送一个消息写入（MSI-X，Message Signaled Interrupt）——本质是一次 PCIe 内存写事务，而非传统 INTx 那样的专用电气信号线——含义是&amp;quot;Queue 5 有包到了，请处理&amp;quot;。这个动作本身几乎没有开销，网卡发完就结束了。</description></item><item><title>低延迟系统的 CPU 核心规划：从 isolcpus 到 Busy Poll 的内核级实战</title><link>https://code-agree.github.io/blog/2026-03-30-cpu_bindcore/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://code-agree.github.io/blog/2026-03-30-cpu_bindcore/</guid><description>上一篇文章《从网线到策略引擎：Linux 网络收包全路径深度解析》解决的是&amp;quot;数据包怎么走&amp;quot;的问题——从物理层到用户态的七个阶段。本文解决的是&amp;quot;CPU 核心怎么分&amp;quot;的问题——在一个多核系统上，哪些核跑内核家务活、哪些核处理网卡中断、哪些核跑行情线程，以及为什么这样分配。
一、问题的起点：能不能把所有核都 isolate 掉？ #在低延迟系统中，isolcpus 是最常见的核心隔离手段。它的作用是将指定的 CPU 从内核调度器的默认调度域中移除，使得普通进程不会被自动调度到这些核上，从而为延迟敏感的用户线程提供&amp;quot;干净&amp;quot;的执行环境。
一个自然的想法是：既然隔离能减少干扰，那把所有核都隔离了，再手动把用户线程绑上去，岂不是最干净？
这样做不合理，甚至可能导致系统无法正常运行。
1.1 内核的&amp;quot;家务活&amp;quot;必须有人干 #Linux 系统的正常运行依赖大量内核线程和基础设施：
ksoftirqd/N — 每个 CPU 上的软中断处理线程 kworker/N:M — 工作队列线程（延迟工作、异步 I/O 等） rcu_preempt — RCU 回调处理 migration/N — 进程迁移 watchdog/N — CPU 死锁检测 systemd (PID 1) — 用户空间初始化 sshd / journald — 基础系统服务 当你用 isolcpus=0-191 把所有 192 个核都隔离后，这些内核线程和系统服务没有任何核可以正常调度。较新的内核在启动早期会检查是否至少存在一个 housekeeping CPU，如果全部隔离，系统可能直接启动失败或行为异常。
1.2 CPU 0 的特殊地位 #即使不全部隔离，把 CPU 0 隔离也需要格外小心。在 Linux 内核中，CPU 0 承担着特殊职责：
Boot CPU：系统启动阶段的大量初始化逻辑绑定在 CPU 0 上执行，部分内核子系统硬编码依赖它。start_kernel() → rest_init() → kernel_init() 这条初始化链路始终在 CPU 0 上运行。</description></item><item><title>从网线到策略引擎：Linux 网络收包全路径深度解析</title><link>https://code-agree.github.io/blog/2026-03-17-net_proc/</link><pubDate>Tue, 17 Mar 2026 23:10:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-17-net_proc/</guid><description>本文以一个真实的加密货币交易系统为背景，系统性讲解 Linux 下一个网络数据包从物理网卡到达用户态应用的完整路径，并结合 AWS Graviton (ARM64) + ENA 网卡 + 192 核环境的实际 IRQ 数据进行分析。
一、全景概览 #一个数据包从交易所服务器发出，到你的策略引擎处理完毕，经历了以下七个阶段：
交易所服务器 │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 阶段 1：物理层 &amp;amp; NIC 硬件 │ │ 网线 → PHY → MAC → On-Chip SRAM暂存 → RSS分流 │ │ → DMA 写入主存 Ring Buffer │ ├─────────────────────────────────────────────────────────┤ │ 阶段 2：硬中断 (Hard IRQ) │ │ NIC 触发中断 → CPU 响应 → 最小化处理后调度 softirq │ ├─────────────────────────────────────────────────────────┤ │ 阶段 3：软中断 &amp;amp; NAPI 轮询 │ │ ksoftirqd / NET_RX_SOFTIRQ → NAPI poll 批量收包 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 4：内核协议栈 │ │ IP 层 → TCP/UDP 层 → 查找 socket → 放入 socket buffer │ ├─────────────────────────────────────────────────────────┤ │ 阶段 5：Socket Buffer │ │ sk_receive_queue 排队等待用户态读取 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 6：用户态唤醒 &amp;amp; 数据拷贝 │ │ epoll_wait 返回 → recv/read → 数据从内核拷贝到用户态 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 7：应用层处理 │ │ JSON 解析 → 策略计算 → 下单 / 信号转发 │ └─────────────────────────────────────────────────────────┘ 下面逐层展开。</description></item><item><title>高频交易中的 WebSocket 架构设计：从阻塞 IO 到事件驱动</title><link>https://code-agree.github.io/blog/2026-03-03-websocket/</link><pubDate>Tue, 03 Mar 2026 23:38:37 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-03-websocket/</guid><description>本文以一个真实的 OKX Fill Receiver 实现为切入点，深入探讨 HFT 场景下 WebSocket 客户端的架构设计，涵盖 Socket 编程基础、epoll 事件驱动模型、Connection/Socket/Thread 的关系，以及面向超低延迟的工程优化。
一、Socket 编程基础：从 fd 到 Connection #1.1 文件描述符 (File Descriptor) 是一切的起点 #在 Unix/Linux 中，一切皆文件。网络连接也不例外——一个 TCP 连接在内核中对应一个 struct socket，在用户态通过一个整数 文件描述符 (fd) 来引用。
一个 TCP 连接的建立过程：
客户端: 服务端: fd = socket(AF_INET, SOCK_STREAM, 0) listen_fd = socket(...) connect(fd, server_addr, ...) bind(listen_fd, addr, ...) │ listen(listen_fd, backlog) │── SYN ──────────────────► │ │◄─────────────── SYN+ACK ── │ │── ACK ──────────────────► conn_fd = accept(listen_fd, .</description></item><item><title>高性能网络I/O优化原理与实战：从网卡队列到CPU绑核的系统优化</title><link>https://code-agree.github.io/blog/2025-08-18-network_queue/</link><pubDate>Mon, 18 Aug 2025 16:07:40 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-08-18-network_queue/</guid><description>高性能网络I/O优化原理与实战：从网卡队列到CPU绑核的系统优化 #前言 #在高频交易、实时通信等对延迟极度敏感的应用场景中，理解和优化网络I/O处理路径至关重要。本文深入探讨从硬件层面的网卡队列机制到操作系统层面的CPU调度优化，为开发者提供系统性的网络性能优化理论基础和实践方法。
1. 网络数据包处理的完整流程 #1.1 数据包从网卡到应用程序的路径 #理解网络I/O优化的前提是掌握数据包处理的完整流程：
1. 网卡接收数据包 ↓ 2. DMA传输到内存 ↓ 3. 硬件中断触发 (IRQ) ↓ 4. 内核网络栈处理 ↓ 5. 数据放入Socket缓冲区 ↓ 6. 应用程序通过系统调用读取数据 在这个流程中，每一步都涉及CPU资源的分配和调度，任何一步的低效都可能成为整体性能的瓶颈。
1.2 传统单队列网卡的局限性 #早期网卡采用单队列设计，所有网络数据包的处理都集中在一个队列中：
所有数据包 → 单个接收队列 → 单个CPU核心处理 → 性能瓶颈 这种设计在多核系统中存在明显问题：
单核心瓶颈：所有网络中断都在单个CPU核心上处理 资源浪费：其他CPU核心无法参与网络处理 扩展性差：网络吞吐量受限于单核心性能 2. 中断机制与IRQ基础 #2.1 什么是IRQ (Interrupt Request) #**IRQ（中断请求）**是计算机系统中硬件设备通知CPU需要处理某个事件的机制。在网络处理中，IRQ是连接硬件和软件的关键桥梁。
中断的基本概念：
中断：硬件设备向CPU发送的信号，表示有事件需要处理 IRQ号：标识不同中断源的唯一编号 中断向量：指向中断服务程序的内存地址 中断优先级：决定多个中断同时发生时的处理顺序 2.2 中断处理的完整流程 #从网卡数据包到CPU处理的中断流程：
1. 网卡接收数据包 ↓ 2. 网卡通过PCIe总线向中断控制器发送IRQ信号 ↓ 3. 中断控制器（APIC/IO-APIC）选择目标CPU ↓ 4. CPU接收中断信号，保存当前上下文 ↓ 5. CPU跳转到中断服务程序（ISR - Interrupt Service Routine） ↓ 6.</description></item><item><title>深入理解Socket类型：从原理到HFT应用的技术分析</title><link>https://code-agree.github.io/blog/2025-08-08-raw_socket/</link><pubDate>Fri, 08 Aug 2025 01:10:05 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-08-08-raw_socket/</guid><description>引言 #在现代网络编程中，Socket作为应用程序与网络协议栈的接口，为开发者提供了不同层次的网络访问能力。从高级的流式传输到底层的数据包控制，不同类型的Socket满足着各种应用场景的需求。本文将深入分析TCP Socket、UDP Socket和Raw Socket三种主要类型，探讨它们的技术原理、性能差异，并在高频交易(HFT)场景下进行实战分析。
Socket类型概述 #Socket本质上是操作系统提供的网络编程接口，它抽象了底层的网络通信细节。根据工作的协议层级和提供的抽象程度，主要分为三种类型：
基本分类 # Socket类型 创建方式 工作层级 抽象程度 应用场景 TCP Socket socket(AF_INET, SOCK_STREAM, 0) 传输层 高 可靠连接通信 UDP Socket socket(AF_INET, SOCK_DGRAM, 0) 传输层 中 无连接快速通信 Raw Socket socket(AF_INET, SOCK_RAW, protocol) 网络层 低 自定义协议开发 网络协议栈与Socket的对应关系 #协议栈层次结构 #应用层 | HTTP, WebSocket, 自定义协议 传输层 | TCP, UDP 网络层 | IP, ICMP 数据链路层 | Ethernet 物理层 | 电信号传输 Socket在协议栈中的位置 #TCP Socket: 完全封装传输层TCP协议，提供可靠的字节流传输。内核自动处理连接管理、流量控制、拥塞控制和数据重传。
UDP Socket: 封装传输层UDP协议，提供简单的数据报传输。内核处理端口管理和基本的错误检测。
Raw Socket: 直接访问网络层，绕过传输层处理。允许应用程序完全控制IP数据包的构造和发送。
各Socket类型详细分析 #TCP Socket (SOCK_STREAM) #工作原理 #TCP Socket基于可靠传输协议，提供面向连接的字节流服务：</description></item><item><title>深度解析UDP高丢包问题：从现象到原理的完整剖析</title><link>https://code-agree.github.io/blog/2025-07-27-network_buffer/</link><pubDate>Sun, 27 Jul 2025 11:36:17 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-27-network_buffer/</guid><description>深度解析UDP高丢包问题：从现象到原理的完整剖析 #在高性能网络应用中，UDP丢包问题是一个常见但复杂的挑战。本文将通过一个真实的案例，从问题现象出发，深入分析丢包的根本原因，并详细解释内核缓冲区与应用层缓冲区的区别和优化策略。
问题现象：91%的惊人丢包率 #测试环境与配置 #我们使用iperf3进行UDP性能测试，配置如下：
测试协议：UDP 目标带宽：100 Mbps 包大小：64字节 测试时长：60秒 客户端：172.28.15.164 服务端：47.83.183.226 (内网地址：192.168.24.102) 测试结果分析 #客户端发送情况（正常）：
Transfer: 715 MBytes (60秒内) Bitrate: 100 Mbits/sec (稳定发送) Total Datagrams: 11,718,940 (0丢失) 服务端接收情况（异常）：
Transfer: 66.4 MBytes (仅接收到9.3%的数据) Bitrate: 9.20 Mbits/sec (带宽骤降90%) Lost/Total: 10,610,551/11,699,051 (91%丢包率) 丢包模式的三个阶段 #通过详细分析服务端日志，我们发现了明显的性能退化模式：
初始阶段（0-1秒）：91.2 Mbps，丢包率0% 过渡阶段（1-5秒）：带宽逐渐下降，丢包率从6.8%急剧增加到75% 稳定阶段（6-60秒）：稳定在3 Mbps，丢包率高达97% 这种模式表明系统在面对高速UDP流量时，从初始的正常处理快速退化为严重的性能瓶颈状态。
根因分析：UDP接收缓冲区不足 #发现关键线索 #通过检查系统配置，我们发现了问题的根源：
$ cat /proc/sys/net/core/rmem_default 212992 $ cat /proc/sys/net/core/rmem_max 212992 关键发现：UDP接收缓冲区仅有208KB，这在高速网络环境中明显不足。
缓冲区容量计算 #让我们计算一下这个缓冲区的实际容量：
目标带宽：100 Mbps = 12.5 MB/s 包大小：64字节（仅载荷） 每秒包数：100 Mbps ÷ (64×8 bits) ≈ 195,312包/秒 208KB缓冲区可存储包数：208,896 ÷ 64 ≈ 3,264包 缓冲区满载时间：3,264 ÷ 195,312 ≈ 0.</description></item><item><title>DPDK为什么必须使用Hugepage：从内存管理本质到架构必需</title><link>https://code-agree.github.io/blog/2025-07-21-hugepage_indpdk/</link><pubDate>Mon, 21 Jul 2025 03:54:37 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-21-hugepage_indpdk/</guid><description>引言 #在高性能网络编程领域，DPDK（Data Plane Development Kit）作为用户态网络驱动框架，能够实现单核数千万PPS（包每秒）的惊人性能。然而，DPDK强制要求使用hugepage的设计决策常常让初学者困惑：为什么不能使用传统的malloc/new分配内存？hugepage究竟解决了什么根本性问题？
本文将从内存管理的底层原理出发，深入分析DPDK使用hugepage的技术必然性，揭示这一设计选择背后的深层架构考量。
1. 澄清关键概念：Hugepage不是&amp;quot;大内存分配&amp;quot; #1.1 常见的概念误区 #许多开发者错误地认为hugepage就是&amp;quot;分配大块内存&amp;quot;的机制，这是对hugepage本质的根本性误解。
错误理解：
// 误以为这就是hugepage的作用 char* large_buffer = malloc(1024 * 1024 * 1024); // 分配1GB内存 正确理解： Hugepage是操作系统内存管理粒度的改变，而不是应用层面的内存分配大小问题。
1.2 Hugepage的本质定义 #标准内存管理：
操作系统以4KB为单位管理物理内存 每个虚拟内存页对应一个4KB的物理内存页 页表项记录虚拟页到物理页的映射关系 Hugepage内存管理：
操作系统以2MB或1GB为单位管理物理内存 每个虚拟内存页对应一个2MB/1GB的物理内存页 大幅减少页表项数量，改变内存管理的基本粒度 1.3 malloc vs hugepage的本质差异 #malloc分配大内存的实际情况 #char* buffer = malloc(1024 * 1024 * 1024); // 分配1GB 虚拟内存视角：
应用程序看到连续的1GB虚拟地址空间 地址范围：[0x100000000 - 0x140000000] 物理内存实际情况：
需要的4KB页面数：1GB ÷ 4KB = 262,144个页面 页表项数量：262,144个 物理内存布局：完全分散，可能遍布整个物理内存空间 典型的物理地址映射： 虚拟页0x100000000 → 物理页0x87654000 虚拟页0x100001000 → 物理页0x12345000 虚拟页0x100002000 → 物理页0x98765000 .</description></item><item><title>深度解析：Asio在Linux平台的I/O模型本质</title><link>https://code-agree.github.io/blog/2025-07-09-asio/</link><pubDate>Wed, 09 Jul 2025 02:19:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-09-asio/</guid><description>在现代C++网络编程中，Boost.Asio（或standalone asio）是使用最广泛的异步I/O库之一。然而，关于Asio的I/O模型本质，特别是在Linux平台上的实现机制，存在很多误解。本文将深入剖析Asio在Linux平台的真实面目。
本质定位 #Asio的真实身份 #Asio在Linux平台上的本质：同步非阻塞I/O + I/O多路复用 + 回调机制的高级封装
这意味着：
✅ 不是传统的阻塞I/O：提供了异步编程接口 ❌ 不是真正的异步I/O：底层仍使用同步系统调用 ✅ 是异步编程框架：通过回调机制模拟异步编程体验 核心理解 #// Asio给你的印象（异步风格API） socket.async_read_some(buffer(data), [](error_code ec, size_t bytes) { // 看起来像异步回调 process_data(data, bytes); }); // 但Linux下的实际执行（简化） epoll_wait(epfd, events, 128, -1); // 同步等待事件 ssize_t n = read(fd, buffer, size); // 同步读取 callback(n); // 调用用户回调 关键洞察：Asio提供了异步的编程体验，但不是异步的执行机制。
工作原理 #事件驱动的执行模型 #Asio在Linux上实现了Reactor模式，而不是Proactor模式：
// Reactor模式的典型流程 class AsioReactor { public: void async_read(socket&amp;amp; s, buffer b, handler h) { // 1. 注册读取意图 register_read_intent(s.</description></item><item><title>I/O模型深度解析：多路复用、同步与异步的本质区别</title><link>https://code-agree.github.io/blog/2025-07-09-bio_nio/</link><pubDate>Wed, 09 Jul 2025 01:57:53 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-09-bio_nio/</guid><description>在高性能网络编程中，I/O模型的选择往往决定了系统的并发能力和性能表现。然而，关于I/O多路复用、同步I/O、异步I/O等概念，许多开发者存在理解上的误区。本文将深入剖析这些概念的本质区别，澄清常见的混淆点。
常见的概念混淆 #误区一：I/O多路复用就是异步I/O #这是最常见的误解。实际上，I/O多路复用本质上仍然是同步I/O。准确地说，多路复用的事件检测阶段是阻塞的（epoll_wait会阻塞），而实际的I/O读写阶段仍是同步操作。它只是提供了&amp;quot;等待多个同步I/O事件 + 显式事件通知&amp;quot;的机制，而不是真正的异步执行。
误区二：非阻塞I/O就是异步I/O #非阻塞I/O（NIO）虽然不会让线程阻塞等待，但仍然是同步I/O，因为应用程序需要主动调用系统调用并立即处理返回结果（包括&amp;quot;暂时无数据&amp;quot;的情况）。
系统层面的I/O模型分类 #1. 同步阻塞I/O (BIO) #特点：应用程序发起I/O调用后，线程阻塞等待操作完成。
int sockfd = socket(AF_INET, SOCK_STREAM, 0); char buffer[1024]; // 线程会阻塞在这里，直到有数据到达 ssize_t n = read(sockfd, buffer, sizeof(buffer)); if (n &amp;gt; 0) { process_data(buffer, n); } 应用场景：
连接数较少的服务 对实时性要求不高的应用 通常配合多线程使用 2. 同步非阻塞I/O (NIO) #特点：应用程序发起I/O调用后立即返回，需要主动检查操作状态。
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 设置为非阻塞模式 int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); char buffer[1024]; ssize_t n = read(sockfd, buffer, sizeof(buffer)); if (n &amp;gt; 0) { // 成功读取数据 process_data(buffer, n); } else if (n == -1 &amp;amp;&amp;amp; errno == EAGAIN) { // 暂时无数据，需要稍后重试 // 这是关键：应用程序需要处理&amp;#34;未完成&amp;#34;状态 } else { // 发生错误 handle_error(); } 关键理解：read()调用立即返回，但返回的可能是&amp;quot;操作状态&amp;quot;而不是&amp;quot;完成的数据&amp;quot;。</description></item><item><title>DPDK + WebSocket客户端内存管理故障深度定位实录</title><link>https://code-agree.github.io/blog/2025-07-05-dpdk_application/</link><pubDate>Sat, 05 Jul 2025 01:38:06 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-05-dpdk_application/</guid><description>问题背景 #在开发基于DPDK的高性能WebSocket客户端时，遇到了典型的内存管理问题。该客户端使用了QuickWS框架，集成F-Stack网络栈和OpenSSL，在连接Binance WebSocket API进行高频数据接收测试时出现段错误。
技术栈概览 # 网络栈: DPDK + F-Stack WebSocket库: QuickWS (自定义高性能框架) SSL/TLS: OpenSSL 3.x 内存分配器: Flash Allocator (自定义分配器) 缓冲区: Ring Buffer with Flash Allocator 目标: 高吞吐量实时数据接收性能测试 故障现象 #Connected to Binance WebSocket stream! fd: 1 Accepted protocols: , extensions: Thread 1 &amp;#34;binance_client&amp;#34; received signal SIGSEGV, Segmentation fault. 定位过程 #第一阶段：环境问题排查 #初始现象: 程序在DPDK初始化阶段就出现问题
EAL: Auto-detected process type: SECONDARY EAL: Fail to recv reply for request /var/run/dpdk/rte/mp_socket:bus_vdev_mp 解决方案: 清理DPDK残留资源
sudo rm -rf /var/run/dpdk/rte/mp_socket* sudo rm -rf /dev/hugepages/* 关键发现: DPDK多进程模式的资源竞争会导致初始化挂起。</description></item><item><title>DPDK性能验证技术分享</title><link>https://code-agree.github.io/blog/2025-07-03-dpdk_check/</link><pubDate>Thu, 03 Jul 2025 04:46:05 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-03-dpdk_check/</guid><description>目录 # DPDK性能优势概述 性能验证维度 系统调用分析 上下文切换监控 CPU使用效率分析 内存访问优化验证 网络性能基准测试 性能指标解读 验证方法总结 1. DPDK性能优势概述 #1.1 传统网络栈 vs DPDK架构 #传统网络栈流程：
应用程序 → Socket API → 内核网络栈 → 网卡驱动 → 硬件 ↑ 系统调用开销 ↑ 内核态/用户态切换 ↑ 数据拷贝 ↑ 中断处理 DPDK流程：
应用程序 → DPDK API → PMD → 硬件 ↑ 用户态直接操作 ↑ 零拷贝 ↑ 轮询模式 ↑ CPU绑定 1.2 理论性能提升 # 优化点 传统方式 DPDK方式 预期提升 延迟 50-100μs 5-20μs 3-10倍 吞吐量 1-5 Gbps 10-100 Gbps 10-20倍 CPU效率 50-70% 80-95% 1.</description></item><item><title>高频交易系统中的WebSocket网络缓冲区优化技术</title><link>https://code-agree.github.io/blog/2025-07-01-tcp_perf/</link><pubDate>Tue, 01 Jul 2025 18:06:02 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-01-tcp_perf/</guid><description>摘要 #本文深入探讨了高频交易系统中WebSocket连接的网络缓冲区优化技术，重点关注极低延迟性能优化。文章详细分析了TCP层优化、Socket缓冲区调优、大页内存应用以及网络栈各层次的性能优化策略，为构建微秒级延迟的交易系统提供了全面的技术指南。
1. 引言 #在现代金融市场中，高频交易系统的竞争优势很大程度上取决于其网络栈的性能。WebSocket作为一种全双工通信协议，已成为高频交易系统连接交易所和市场数据提供商的重要技术。然而，标准WebSocket实现通常无法满足高频交易对极低延迟的苛刻要求，这些系统需要微秒级别的响应时间。
本文旨在提供一个全面的网络缓冲区优化框架，从TCP底层协议到WebSocket应用层，系统性地探讨如何将延迟降至最低，尤其是通过优化网络缓冲区结构和内存访问模式。
2. 网络栈基础概念与缓冲区架构 #2.1 TCP与Socket的关系与区别 #在深入优化之前，需要明确TCP与Socket这两个核心概念的区别与联系：
概念层面 #TCP (传输控制协议):
是一种通信协议，定义了数据如何在网络上可靠传输的规则 是OSI模型中的传输层协议 规定了如何建立连接、传输数据、处理丢包、确保顺序、流量控制等机制 是一组规则和标准，而非具体实现 Socket (套接字):
是一个编程接口/抽象，是应用程序与网络协议交互的途径 可以看作是网络通信的&amp;quot;端点&amp;quot; 是操作系统提供的API，让应用程序能够使用网络功能 Socket不仅可以使用TCP，还可以使用UDP、Unix域等协议 比喻说明 #可以通过这个比喻理解：
TCP是一种语言和交流规则(如中文+礼仪规范) Socket是允许人们使用这种语言交流的电话机 代码层面区别 #TCP体现为协议参数和行为：
// 这些是TCP协议相关的选项 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &amp;amp;flag, sizeof(flag)); setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, &amp;amp;flag, sizeof(flag)); Socket体现为创建和管理通信端点：
// 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM指定TCP协议 // 设置套接字选项 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &amp;amp;rcvbuf, sizeof(rcvbuf)); // 连接、发送、接收数据 connect(sockfd, ...); send(sockfd, ...); recv(sockfd, ...); 缓冲区层面的区别 #TCP缓冲区:</description></item><item><title>TLS会话恢复（Session Resumption）</title><link>https://code-agree.github.io/blog/2025-06-24-session_resumption_techniques/</link><pubDate>Fri, 07 Mar 2025 19:24:12 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-session_resumption_techniques/</guid><description>1. 会话恢复简介 #什么是会话恢复？ #TLS会话恢复是TLS协议的一项优化特性，允许客户端和服务器基于之前建立的安全会话快速恢复通信，跳过完整的握手过程。在TLS 1.3中，会话恢复主要通过**PSK（Pre-Shared Key，预共享密钥）**机制实现，而在TLS 1.2及更早版本中，也可以通过Session ID或Session Ticket实现。
为什么需要会话恢复？ # 性能优化： 完整握手（TLS 1.3）：1-RTT 会话恢复：1-RTT（或0-RTT） 显著减少连接建立时间 资源节省： 降低CPU开销（避免重复密钥交换） 减少网络带宽占用 2. TLS 1.3中的会话恢复机制 #工作流程对比 #完整握手（TLS 1.3） #Client Server | ClientHello | |--------------------&amp;gt;| | ServerHello | | EncryptedExt | | Certificate | | CertVerify | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| | NewSessionTicket | |&amp;lt;--------------------| RTT：1次往返 服务器在握手后发送NewSessionTicket，包含PSK和有效期信息。 会话恢复（1-RTT） #Client Server | ClientHello | | (with PSK) | |--------------------&amp;gt;| | ServerHello | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| RTT：1次往返 客户端使用之前保存的PSK直接恢复会话。 0-RTT（可选） #Client Server | ClientHello | | (with PSK + Early Data) | |--------------------&amp;gt;| | ServerHello | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| RTT：0次往返（早期数据随首次请求发送） 注意：0-RTT有重放攻击风险，仅适用于幂等请求。 3.</description></item><item><title>WebSocket消息处理线程CPU亲和性导致的消息阻塞故障分析</title><link>https://code-agree.github.io/blog/2025-06-24-message_queue_overstocking_solutions/</link><pubDate>Fri, 13 Dec 2024 05:15:51 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-message_queue_overstocking_solutions/</guid><description>一、故障现象 #1.1 单endpoint模式故障 # 单个WebSocket连接时消息接收完全阻塞 日志显示消息处理线程启动后无法接收新消息 [2024-12-12 20:31:29.455] [error] [setThreadAffinity] Error calling pthread_setaffinity_np: 22 [2024-12-12 20:31:29.697] [info] Message thread started for endpoint: OkxPublic // 之后无消息接收日志 1.2 多endpoint模式部分正常 # 多个WebSocket连接时只有一个线程能正常接收消息 日志显示消息处理情况： [20:54:50.542] [thread 91374] Processing message for OkxPublic [20:54:50.640] [thread 91374] Processing message for OkxPublic // 只有一个线程在持续处理消息 二、系统架构分析 #2.1 WebSocket消息接收机制 #void WebSocketClient::receiveMessages(const MessageHandler&amp;amp; handler) { while (true) { try { // 1. 阻塞式接收WebSocket消息 int n = ws_-&amp;gt;receiveFrame(buffer.data(), buffer.size(), flags); // 2. 同步回调处理消息 if (n &amp;gt; 0) { handler(buffer.</description></item><item><title>高性能网络编程：io_uring 与内存优化技术详解</title><link>https://code-agree.github.io/blog/2025-06-24-io_uring_basics/</link><pubDate>Fri, 06 Dec 2024 06:04:25 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-io_uring_basics/</guid><description>0. 内存管理优化 #0.1 大页内存 (Huge Pages) #大页内存是一种内存管理优化技术，主要优势：
减少 TLB (Translation Lookaside Buffer) 缺失 减少页表项数量 提高内存访问效率 系统配置和检查：
# 检查系统大页配置 cat /proc/meminfo | grep Huge # 配置大页 echo 20 &amp;gt; /proc/sys/vm/nr_hugepages # 分配20个大页 0.2 内存锁定 (Memory Locking) #防止内存被交换到磁盘，确保数据始终在物理内存中：
# 检查内存锁定限制 ulimit -l # 修改限制（需要root权限） echo &amp;#34;* soft memlock unlimited&amp;#34; &amp;gt;&amp;gt; /etc/security/limits.conf 0.3 内存优化实现 #struct IOBuffer { char* data; size_t size; explicit IOBuffer(size_t s) : size(s) { // 1. 尝试使用大页内存 data = static_cast&amp;lt;char*&amp;gt;(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0)); if (data == MAP_FAILED) { // 2.</description></item><item><title>高频交易场景下的多WS连接低延时方案设计</title><link>https://code-agree.github.io/blog/2025-06-24-multi_quote_data_processing/</link><pubDate>Tue, 03 Dec 2024 01:01:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-multi_quote_data_processing/</guid><description>1. 业务背景与挑战 #在高频交易系统中，需要同时维护多个WebSocket连接以订阅不同交易所的行情数据。主要挑战包括：
需要处理多个交易所的并发连接 对消息处理延迟有严格要求 需要保证数据处理的稳定性 系统资源（CPU、内存）的高效利用 2. 传统方案的局限 #2.1 传统消息队列方案 #// 常见的消息处理流程 WebSocket接收 -&amp;gt; 消息队列 -&amp;gt; 处理线程池 -&amp;gt; 业务处理 存在的问题：
消息经过队列带来额外延迟 线程切换开销大 内存拷贝次数多 资源竞争导致性能不稳定 3. 优化方案设计 #3.1 核心设计理念 # 零拷贝数据处理 CPU亲和性绑定 预分配内存 每个连接独立处理 3.2 关键组件设计 #struct ConnectionContext { // 连接基础信息 std::shared_ptr&amp;lt;WebSocketClient&amp;gt; client; std::string endpoint_name; // 性能优化相关 int cpu_core{-1}; // CPU核心绑定 char* direct_buffer{nullptr}; // 预分配缓冲区 static constexpr size_t BUFFER_SIZE = 64 * 1024; std::shared_ptr&amp;lt;MessageProcessor&amp;gt; dedicated_processor; // 资源管理 ~ConnectionContext() { if (direct_buffer) { munlock(direct_buffer, BUFFER_SIZE); munmap(direct_buffer, BUFFER_SIZE); } } // 禁用拷贝以保证资源安全 ConnectionContext(const ConnectionContext&amp;amp;) = delete; ConnectionContext&amp;amp; operator=(const ConnectionContext&amp;amp;) = delete; }; 3.</description></item><item><title>行情链路上的队列取舍与 WebSocket 接收优化</title><link>https://code-agree.github.io/blog/2025-06-24-advanced_queue_usage_patterns/</link><pubDate>Sun, 15 Sep 2024 04:03:51 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-advanced_queue_usage_patterns/</guid><description>行情链路上的队列取舍与 WebSocket 接收优化 #在高频交易(HFT)系统中,行情从交易所 WebSocket 到策略引擎的链路上,每一微秒的延迟都可能转化为实际的经济损失。本文讨论这条链路上的两个关键决策:WebSocket 消息接收机制怎么设计,以及行情数据要不要经过队列。
1. WebSocket消息接收机制优化 #在高频交易系统中,每一毫秒的延迟都可能导致巨大的经济损失。因此,优化WebSocket消息的接收机制对于系统的整体性能至关重要。
1.1 WebSocketClient类设计与实现 #以下是一个高效的WebSocketClient类的实现示例:
class WebSocketClient { public: using MessageHandler = std::function&amp;lt;void(const char*, size_t)&amp;gt;; WebSocketClient(/* 构造函数参数 */) : ws_(nullptr), running_(false) {} void receiveMessages(MessageHandler handler) { if (!ws_) { throw std::runtime_error(&amp;#34;WebSocket is not connected&amp;#34;); } constexpr size_t BUFFER_SIZE = 1024 * 1024; // 1MB buffer std::array&amp;lt;char, BUFFER_SIZE&amp;gt; buffer; int flags; while (running_) { try { int n = ws_-&amp;gt;receiveFrame(buffer.data(), buffer.size(), flags); if (n &amp;gt; 0) { handler(buffer.</description></item></channel></rss>