在 Linux 中,虚拟内存不仅仅是地址转换,它是一套复杂的资源管理机制。以下是作为 BSP 工程师最需要掌握的核心概念和调试手段。


1. Linux 虚拟内存布局 (Memory Layout)

Linux 将虚拟地址空间划分为两大部分:用户空间 (User Space)内核空间 (Kernel Space)

  • 用户空间:每个进程都有独立的 $2^{N}$ 虚拟空间(32位系统为 3GB,64位系统巨大)。

  • 内核空间:所有进程共享同一个内核映射。

    • Lowmem (直接映射区):物理内存的前一部分直接线性映射到内核虚拟地址。

    • Vmalloc 区:用于非连续物理内存的映射,适合分配大块缓冲区。

2. 核心机制:从“静态映射”到“动态按需”

你在裸机中可能是预先写死页表,而 Linux 是 “按需分页” (Paging on demand)

  • 页表 (Page Tables):Linux 使用多级页表(如 ARM64 通常是 4 级:PGD -> PUD -> PMD -> PTE)。

  • 缺页异常 (Page Fault):当你访问一个尚未建立映射的虚拟地址时,内核会触发缺页异常,在异常处理函数中实时分配物理页并填充页表。

3. BSP 工程师必看的内存节点

在调试 Camera 数据流或大块 DMA 缓冲区时,你需要通过以下节点监控内存状态。

/proc/meminfo (全局视角)
  • 用途:查看系统总内存使用情况。

  • 关键指标

    • MemTotal: 总物理内存。

    • MemAvailable: 真正可用的内存(比 Free 更准确,因为它计入了可回收的 Cache)。

    • AnonPages: 进程使用的匿名内存(堆、栈)。

/proc# cat /proc/meminfo

ShmemPmdMapped:        0 kB
FileHugePages:         0 kB
FilePmdMapped:         0 kB
CmaTotal:              0 kB
CmaFree:               0 kB
Unaccepted:            0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
DirectMap4k:       66332 kB
DirectMap2M:     5173248 kB
DirectMap1G:    10485760 kB

/proc/[pid]/maps & smaps (进程视角)
  • 用途:查看某个具体进程(如你的 AI 视觉处理进程)的内存映射。

  • 实战:通过 cat /proc/self/maps 可以看到代码段、数据段、堆以及共享库(.so)分别映射在什么虚拟地址。

/proc# cat 10143/maps
59865aa00000-59865aa19000 r--p 00000000 07:00 1049563                    /usr/bin/tmux
59865aa19000-59865aab0000 r-xp 00019000 07:00 1049563                    /usr/bin/tmux

/lib/x86_64-linux-gnu/libevent_core-2.1.so.7.0.1
7d00df3d7000-7d00df3e5000 r--p 00000000 07:00 5112886                    /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
7d00df3e5000-7d00df3f6000 r-xp 0000e000 07:00 5112886                    /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
7d00df3f6000-7d00df404000 r--p 0001f000 07:00 5112886                    /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
7d00df404000-7d00df408000 r--p 0002c000 07:00 5112886                    /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
7d00df408000-7d00df409000 rw-p 00030000 07:00 5112886                    /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3
7d00df409000-7d00df40a000 r--p 00000000 07:00 1079497                    /usr/lib/x86_64-linux-gnu/libutempter.so.1.2.1
7d00df40a000-7d00df40b000 r-xp 00001000 07:00 1079497                    /usr/lib/x86_64-linux-gnu/libutempter.so.1.2.1
7d00df40b000-7d00df40c000 r--p 00002000 07:00 1079497                    /us
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7d00df44a000-7d00df455000 r--p 0002c000 07:00 5112750                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7d00df456000-7d00df458000 r--p 00037000 07:00 5112750                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7d00df458000-7d00df45a000 rw-p 00039000 07:00 5112750                    /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffc9b7c1000-7ffc9b7e2000 rw-p 00000000 00:00 0                          [stack]
7ffc9b7ec000-7ffc9b7f0000 r--p 00000000 00:00 0                          [vvar]
7ffc9b7f0000-7ffc9b7f2000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]

/proc/buddyinfo (物理内存碎片)
  • 用途:查看内核伙伴系统(Buddy System)的空闲页分布。

  • 排查逻辑:如果你在 Bring-up 驱动时发现 kmalloc 分配大块内存失败,即使总内存够用,看这里也能发现是否是因为内存碎片严重(高 order 的空闲块为 0)。

4. 驱动开发中的关键接口

你在做 BSP 调试时,经常会用到这几个函数,它们是操作虚拟内存的“抓手”:

  1. ioremap()

    • 场景:将硬件寄存器的物理地址(PA)映射到内核虚拟地址(VA),以便 CPU 读写寄存器。

  2. mmap() (用户态)

    • 场景:将设备内存(如 Camera Buffer)直接映射到用户空间,实现零拷贝 (Zero-copy)

  3. dma_alloc_coherent()

    • 场景:分配物理连续且关闭 Cache 的内存,用于 DMA 传输。


Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐