<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Memory on Yu's Space</title><link>https://code-agree.github.io/tags/memory/</link><description>Recent content in Memory on Yu's Space</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 25 Aug 2026 09:00:00 +0800</lastBuildDate><atom:link href="https://code-agree.github.io/tags/memory/index.xml" rel="self" type="application/rss+xml"/><item><title>虚拟内存与 Page Table:从一次访存看懂 MMU、TLB、Page Fault 的完整机制</title><link>https://code-agree.github.io/blog/2026-08-25-page_table/</link><pubDate>Tue, 25 Aug 2026 09:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-25-page_table/</guid><description>进程隔离、共享内存、huge page、lazy allocation、段错误——这些看似独立的现象,底层是同一个机制的不同侧面:page table(页表)。本文从零把它讲透,并在最后把上述现象逐一&amp;quot;接回&amp;quot;这张表。
0. 起点:程序里的地址全是虚拟的 #你代码里的每个指针、每个 &amp;amp;变量,都是 virtual address(虚拟地址)——不是内存条上的真实位置。CPU 拿到它不能直接访问 RAM,必须先翻译成 physical address(物理地址)。
为什么要虚拟化?直接用物理地址的世界没法过:所有程序挤在同一片真实内存里互相可踩(无隔离)、程序必须预知自己被装载到哪(无法重定位)。虚拟化后,每个进程都以为自己独占一条平坦的地址空间,真实内存的分配、位置、给不给,由 OS 在翻译层暗中操作。
1. Page 与 page table:按页翻译的字典 #翻译不能按字节做(字典会比内存还大),所以按 page(页) 做:
虚拟地址空间切成 4KB 一页 → page 物理内存切成 4KB 一块 → page frame(页帧) page table = &amp;ldquo;virtual page number → physical frame number&amp;rdquo; 的对照字典,每个进程一本 字典的每个词条 = PTE(page table entry) virtual address (64bit) = [ virtual page number ][ offset(12bit) ] │查 page table │原样保留 ▼ ▼ physical address = [ physical frame number ][ offset ] 每一次访存(每条 load/store)都要经过这次翻译,执行者是 CPU 里的硬件单元 MMU(Memory Management Unit)——&amp;ldquo;隔离由硬件强制&amp;quot;就是这个字面意思。</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>深入理解Hugepage与TLB：原理、机制与性能优化</title><link>https://code-agree.github.io/blog/2025-07-21-hugepage/</link><pubDate>Mon, 21 Jul 2025 03:07:49 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-21-hugepage/</guid><description>引言 #在现代高性能计算中，内存访问性能往往成为应用程序的瓶颈。虽然CPU性能在摩尔定律驱动下快速提升，但内存访问延迟的改善相对缓慢，导致了著名的&amp;quot;内存墙&amp;quot;问题。为了缓解这一问题，现代处理器和操作系统引入了多种机制，其中TLB（Translation Lookaside Buffer）和Hugepage是两个关键的技术。
本文将深入探讨这两种技术的工作原理，以及它们如何协同工作来提升系统性能。
1. 虚拟内存基础 #1.1 虚拟内存系统概述 #现代操作系统普遍采用虚拟内存管理，每个进程都拥有独立的虚拟地址空间。虚拟地址需要通过页表（Page Table）转换为物理地址才能进行实际的内存访问。
1.2 x86-64页表结构 #在x86-64架构中，虚拟地址使用48位有效位，采用4级页表结构：
虚拟地址 (48位有效位)： [47:39] PML4索引 (9位) -&amp;gt; PML4表 (Page Map Level 4) [38:30] PDPT索引 (9位) -&amp;gt; 页目录指针表 (Page Directory Pointer Table) [29:21] PD索引 (9位) -&amp;gt; 页目录表 (Page Directory) [20:12] PT索引 (9位) -&amp;gt; 页表 (Page Table) [11:0] 页内偏移 (12位) -&amp;gt; 4KB页面内偏移 1.3 地址转换过程 #标准4KB页面的地址转换需要遍历完整的4级页表：
physical_addr translate_address(virtual_addr vaddr) { // 1. 从CR3寄存器获取PML4表基址 pml4_entry = PML4_BASE + ((vaddr &amp;gt;&amp;gt; 39) &amp;amp; 0x1FF) * 8; // 2.</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>共享内存多进程通信中的页面切换同步问题分析与解决</title><link>https://code-agree.github.io/blog/2025-06-24-fix_shared_page_position/</link><pubDate>Fri, 17 Jan 2025 04:21:23 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-fix_shared_page_position/</guid><description>问题现象 #在多进程共享内存通信中，发现读取进程出现异常：
写入进程（线程3002707）正常写入数据
读取进程（线程3002791）卡在固定位置：
page: 0 write_pos: 134209160 read_pos: 134199368 问题定位过程 #1. 初步分析 #首先观察到一个关键现象：
Binance的读写正常 Bitget的读取卡在固定位置 两个交易所使用相同的共享内存机制 2. 代码分析 #检查共享内存管理的核心类：
写入机制： template&amp;lt;typename T&amp;gt; bool write(const TypedFrame&amp;lt;T&amp;gt;&amp;amp; frame) { // ... if (write_pos + frame_size &amp;gt; page_size_) { switchToNextPage(); write_pos = current_write_pos_.load(std::memory_order_relaxed); continue; } // ... std::atomic&amp;lt;size_t&amp;gt;* shared_write_pos = reinterpret_cast&amp;lt;std::atomic&amp;lt;size_t&amp;gt;*&amp;gt;(current_page_-&amp;gt;getData()); shared_write_pos-&amp;gt;store(write_pos + frame_size, std::memory_order_release); } 页面切换： void Journal::switchToNextPage() { current_page_ = page_engine_-&amp;gt;getNextPage(); current_write_pos_.store(0, std::memory_order_relaxed); } Page* PageEngine::getNextPage() { current_page_index_++; if (current_page_index_ &amp;gt;= pages_.</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>内存映射（mmap）与零拷贝技术：深入理解和实践</title><link>https://code-agree.github.io/blog/2025-06-24-zero_copy_optimization/</link><pubDate>Tue, 22 Oct 2024 01:23:46 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-zero_copy_optimization/</guid><description>1. 概述 #内存映射（mmap）是一种将文件或设备映射到内存的方法，而零拷贝是一种减少或避免数据在内核空间和用户空间之间不必要复制的技术。这两个概念密切相关，但又有所不同。
2. mmap 是零拷贝吗？ #答案是：mmap 本身不是零拷贝技术，但它可以实现零拷贝的效果。
2.1 mmap 的工作原理 # 当调用 mmap 时，操作系统会在虚拟内存中创建一个新的内存区域。 这个内存区域会映射到文件系统缓存（page cache）中的物理页面。 当程序访问这个内存区域时，如果相应的页面不在内存中，会触发缺页中断，操作系统会从磁盘加载数据到内存。 2.2 为什么 mmap 可以实现零拷贝 # 一旦映射建立，用户进程可以直接读写这个内存区域，而无需在用户空间和内核空间之间进行数据复制。 对于读操作，数据从磁盘读入 page cache 后，可以直接被用户进程访问，无需额外复制。 对于写操作，修改直接发生在 page cache 上，操作系统会在适当的时候将修改同步到磁盘。 3. mmap 与传统 I/O 的比较 #3.1 传统 read 系统调用 #char buffer[4096]; ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); 这个过程涉及两次数据拷贝：
从磁盘到内核缓冲区 从内核缓冲区到用户空间缓冲区 3.2 使用 mmap #void* addr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); // 直接访问 addr 指向的内存 mmap 减少了一次数据拷贝。其原理是将用户空间的虚拟地址映射到页缓存（page cache）的物理页上，数据仍然先从磁盘加载到页缓存，但用户进程直接访问的就是页缓存中的同一份数据，无需再从内核缓冲区拷贝到用户缓冲区。</description></item><item><title>故障复盘报告：内存映射文件中的 std::string 导致的段错误</title><link>https://code-agree.github.io/blog/2025-06-24-string_memory_mapping_techniques/</link><pubDate>Thu, 12 Sep 2024 15:23:23 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-string_memory_mapping_techniques/</guid><description>故障复盘报告：内存映射文件中的 std::string 导致的段错误 #1. 问题描述 #在使用内存映射文件存储订单数据的过程中，程序在重启后出现段错误。具体表现为在尝试访问存储在内存映射文件中的 Order 结构体的 id 字段时，程序崩溃。
2. 错误信息 #程序崩溃时的 GDB 调试信息如下：
Thread 2 &amp;#34;strategyandtrad&amp;#34; received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff6f4c6c0 (LWP 446582)] __memcmp_sse2 () at ../sysdeps/x86_64/multiarch/memcmp-sse2.S:258 258 ../sysdeps/x86_64/multiarch/memcmp-sse2.S: No such file or directory. (gdb) bt #0 __memcmp_sse2 () at ../sysdeps/x86_64/multiarch/memcmp-sse2.S:258 #1 0x000055555556d79b in std::char_traits&amp;lt;char&amp;gt;::compare (__s1=0x7f4710000eb0 &amp;lt;error: Cannot access memory at address 0x7f4710000eb0&amp;gt;, __s2=0x7fffe8000c80 &amp;#34;ORD-1726124231791862593&amp;#34;, __n=23) at /usr/include/c++/12/bits/char_traits.h:385 #2 0x000055555559c599 in std::operator==&amp;lt;char&amp;gt; (__lhs=&amp;lt;error: Cannot access memory at address 0x7f4710000eb0&amp;gt;, __rhs=&amp;#34;ORD-1726124231791862593&amp;#34;) at /usr/include/c++/12/bits/basic_string.</description></item></channel></rss>