当前位置: 首页 > news >正文

Linux 系统的内存分布结构及其之间的关系(持续更新)

Linux 系统的内存分布结构(memory layout / memory organization)是理解操作系统内核、驱动、应用程序内存管理的基础之一。下面将从宏观到微观、从物理内存到虚拟内存的角度,系统地梳理整个结构。


一、整体框架:物理内存 vs 虚拟内存

类型说明典型范围(x86_64)
物理内存(Physical Memory)实际存在的内存条上的存储空间0x0000_0000 ~ 物理RAM最大地址
虚拟内存(Virtual Memory)每个进程或内核看到的逻辑地址空间,由MMU映射到物理内存用户态通常 0x0000_0000_0000_0000 ~ 0x0000_7fff_ffff_ffff

Linux 中每个进程看到的虚拟内存空间都是独立的,通过 页表(Page Table) 映射到共享的物理内存。


二、Linux 内核视角下的内存布局

(1)物理内存布局

在 Linux 启动后,物理内存被划分为以下几个区域(在 /proc/zoneinfo 可以看到):

区域名作用对应体系结构
ZONE_DMA旧式设备需要的低端物理内存(<16MB)x86等老平台
ZONE_DMA32需要 32 位地址的 DMA 设备使用(<4GB)x86_64平台
ZONE_NORMAL普通内存区,内核直接映射通常从 4GB 开始
ZONE_HIGHMEM高端内存,内核需要临时映射才能访问(32位系统)仅32位平台

在 64 位 Linux 系统上,通常没有 HIGHMEM,因为虚拟地址空间够大,全部物理内存都可以直接映射。


(2)虚拟地址空间布局(内核空间)

Linux 内核的虚拟地址空间分为:

区域作用典型地址(x86_64)
内核空间 (kernel space)共享于所有进程,由内核使用ffff800000000000 ~ ffffffffffffffff
直接映射区 (direct mapping region)将所有物理内存映射到虚拟空间通常 ffff888000000000 开始
vmalloc 区动态分配非连续物理内存的虚拟区域ffffc90000000000 ~ ffffe8ffffffffff
模块加载区 (module region)内核模块加载的虚拟地址ffffffffa0000000
fixmap 区固定映射的一些特殊页(如APIC、IO空间)高端地址附近

三、用户进程的虚拟内存布局

每个用户进程的虚拟地址空间大致如下(可通过 /proc/<pid>/maps 查看):

+--------------------------+  高地址
| stack (栈区)            |  <-- 向下增长
+--------------------------+
| mmap() 映射区            |
| (共享库、匿名映射等)     |
+--------------------------+
| heap (堆区)              |  <-- malloc/brk 向上增长
+--------------------------+
| data 段 (已初始化全局变量) |
+--------------------------+
| bss 段 (未初始化全局变量)  |
+--------------------------+
| text 段 (代码段、只读区)   |
+--------------------------+
| NULL (未映射区)          |
+--------------------------+  低地址

用户空间和内核空间之间通常以 TASK_SIZE 分界,比如:

  • 32位系统:TASK_SIZE = 0xC0000000(3GB用户 + 1GB内核)
  • 64位系统:用户空间最大约 128TB 左右(取决于架构)

四、页表与地址转换

CPU 通过 MMU(内存管理单元) 把虚拟地址转换为物理地址。Linux 使用 多级页表结构(4级或5级):

虚拟地址↓
PGD → P4D → PUD → PMD → PTE → 物理页帧

每一级页表指向下一层的页表基址。每个页的典型大小:

  • 普通页:4KB
  • 大页(HugePage):2MB 或 1GB

五、查看与分析命令

命令用途
cat /proc/meminfo系统内存总览
cat /proc/zoneinfo内存分区(ZONE)详情
cat /proc/iomem物理内存映射
free -h查看系统内存使用
vmstat, top, htop动态查看内存
pmap <pid>查看进程虚拟内存分布
cat /proc/<pid>/maps进程内存映射详情

六、简化示意图(x86_64)

物理内存:
+-------------------------+
| BIOS/设备保留区         |  < 1MB
+-------------------------+
| DMA区 (ZONE_DMA)        |  < 16MB
+-------------------------+
| DMA32区 (ZONE_DMA32)    |  < 4GB
+-------------------------+
| 普通区 (ZONE_NORMAL)    |  > 4GB
+-------------------------+内核虚拟地址空间:
ffff800000000000 ───────────────
│  fixmap, modules, vmalloc     │
│  direct mapping of all RAM    │
│  内核页表、堆栈、代码、数据等   │
───────────────────────────────
0000000000000000 ───────────────
│  用户空间 (text, data, heap,  │
│  stack, mmap, libc, etc.)     │
───────────────────────────────

这三个概念之间确实容易混淆,但它们其实是**“同一个内存系统在不同抽象层次上的三种视角”**。


七、三者的逻辑关系(由底向上)

物理内存(Physical Memory)↑│   MMU页表映射(页级映射、权限控制)│
虚拟内存(Virtual Memory)↑│   内核为不同进程划分虚拟地址空间(空间隔离)│
虚拟地址空间布局(Address Space Layout)↑│   每个进程看到的内存分布(代码段/堆/栈)│
用户进程的虚拟内存布局(User Process Memory Layout)

也就是说:

  • 物理内存 是硬件层面上真实存在的存储单元;
  • 虚拟内存 是 CPU + OS 提供的逻辑抽象,让每个进程都“以为自己独占整块内存”;
  • 虚拟地址空间布局 是虚拟内存被 OS 组织的结构;
  • 用户进程的虚拟内存布局 是其中“属于用户态部分”的子集。

八、逐层详解与对应关系

1. 物理内存(Physical Memory)
  • 硬件真实存在:即 DRAM 芯片中的存储单元。
  • 每个物理页帧有一个编号(PFN,Page Frame Number)。
  • OS 通过 页表 把“虚拟页”映射到“物理页帧”。

例:

物理地址:
[0x00000000 - 0x001FFFFF] -> 2MB
[0x00200000 - 0x003FFFFF] -> 2MB
...

2. 虚拟内存(Virtual Memory)
  • MMU(内存管理单元) + 页表(page table) 实现。
  • 每个进程拥有独立的虚拟地址空间(通常64位系统为128TB)。
  • 虚拟页(Virtual Page)通过页表映射到物理页(Physical Page)。

🧩 关键特征:隔离 + 保护

  • 每个进程互不干扰;
  • 访问非法页 → 触发页错误(segfault)。

3. 虚拟地址空间布局(Address Space Layout)

虚拟内存不是乱放的,Linux 内核会为其分区:

  • 用户空间(User Space)
  • 内核空间(Kernel Space)

典型划分(x86_64):

用户空间:  0x0000_0000_0000_0000  ~  0x0000_7fff_ffff_ffff
内核空间:  0xffff_8000_0000_0000  ~  0xffff_ffff_ffff_ffff

这称为虚拟地址空间布局
它描述了“整个虚拟内存范围”的全局分布。


4. 用户进程的虚拟内存布局(User Process Memory Layout)

在用户空间内部,Linux 又进一步划分为:

区域说明典型增长方向
text程序代码段固定
data已初始化的全局变量固定
bss未初始化的全局变量固定
heap动态分配区(malloc/brk)向上增长 ↑
mmap动态映射区(共享库/匿名映射)向下增长 ↓
stack线程栈向下增长 ↓

简图如下:

高地址
+------------------+
| Stack            |  向下增长
+------------------+
| Mapped files     |
+------------------+
| Heap             |  向上增长
+------------------+
| BSS, Data, Text  |
低地址

九、 三者之间的关系总结

层次描述映射/包含关系
物理内存真正的RAM硬件被“页表”映射到虚拟内存
虚拟内存OS提供的逻辑内存抽象由物理页 + 交换区(swap)组成
虚拟地址空间布局整个虚拟内存空间的结构(用户 + 内核)定义虚拟内存的全局分区
用户进程虚拟内存布局用户空间部分的细分结构(代码、堆、栈等)是虚拟地址空间的一部分

十、 举个例子:进程访问内存时的路径

当一个用户进程执行 x = *p; 时:

1️⃣ p 是一个 虚拟地址(属于用户虚拟空间)。
2️⃣ MMU 查页表 → 得到对应的 物理页帧号(PFN)
3️⃣ 访问该物理页中的内容。
4️⃣ 如果该页还没在内存中(比如在磁盘swap),会触发缺页中断,由内核加载后再返回。

即:

虚拟地址(用户视角)↓  页表映射
物理地址(硬件视角)↓  存取DRAM
实际数据

👉 虚拟内存(Virtual Memory) 是操作系统为了解决“内存有限、程序多样、安全隔离、方便编程”等问题而引入的抽象层
它让每个进程都“以为”自己拥有独立且连续的完整内存空间。


十一、为什么需要虚拟内存(动机)

假设我们没有虚拟内存,程序直接访问物理内存地址

  • 程序A加载到物理地址 0x00000000
  • 程序B加载到物理地址 0x00100000

这样会导致一堆问题

问题说明
① 内存冲突问题不同程序可能访问同一物理地址,互相覆盖数据。
② 内存分配困难程序加载地址不连续,碎片化严重。
③ 安全隔离困难程序可以随意访问别的程序或内核空间。
④ 多任务调度麻烦程序切换时需要保存和恢复物理地址映射。
⑤ 扩展性差程序必须适应“物理内存大小限制”。

于是,操作系统与硬件设计者提出了一个“虚拟化”的方案👇

✅ “让每个进程看到的地址空间都一样,但实际背后映射到不同的物理内存。”


十二、虚拟内存的核心思想

1️⃣ 地址虚拟化(Address Virtualization)

每个进程看到的内存空间是“虚拟地址空间”,
通过 MMU(Memory Management Unit) + 页表(Page Table)
把虚拟地址 → 物理地址 映射。

🧩 每个进程都以为:

0x00000000 处是程序的起点

实际上不同进程的 0x00000000 会被映射到不同的物理页。


2️⃣ 页式管理(Paging)

把内存分成固定大小的页(通常4KB):

  • 虚拟内存页(Virtual Page)
  • 物理页帧(Physical Frame)

操作系统通过页表控制两者映射关系。
这样:

  • 虚拟内存可以不连续
  • 物理内存可以动态调度
  • 程序可以只加载部分页

3️⃣ 结合磁盘(Swap / Page Cache)

虚拟内存不仅映射到物理RAM,也可以映射到磁盘文件交换区(swap)
当物理内存不足时,不常用的页可以被换出(swap out),
再次访问时再换入(page fault + swap in)。

🧠 这让程序“看起来”拥有的内存空间远大于实际物理内存!


十三、虚拟内存带来的主要优势

目的描述举例
内存保护(隔离)每个进程拥有独立的地址空间,互不干扰程序A无法访问程序B的内存
简化编程模型程序认为自己有连续内存,不需关心物理碎片数组/指针可直接用线性地址
支持多任务并发操作系统可自由调度不同进程,共享物理内存任务切换只需切页表
内存扩展(虚拟化)可以使用磁盘模拟更多内存即使RAM只有8GB,也能运行10GB程序
共享内存机制允许部分页映射到相同物理页多进程共享libc.so库
高效I/O映射设备寄存器、文件可映射到地址空间mmap()机制

十四、举个直观例子

假设你的电脑只有 4GB RAM
但你同时运行:

  • Chrome:需要 2GB
  • VSCode:需要 1GB
  • Python:需要 2GB

物理内存总需求 5GB > 实际4GB
👉 通过虚拟内存:

进程虚拟地址空间实际物理页交换区(磁盘)
Chrome8GB部分页驻留RAM冷页换出
VSCode8GB部分页驻留RAM冷页换出
Python8GB部分页驻留RAM冷页换出

每个进程都认为自己“拥有”8GB地址空间,
但 OS 只把当前活跃的部分页保留在 RAM 中,
其他暂时不用的页放到磁盘中。


十五、简化结构图(逻辑示意)

用户视角 (虚拟地址空间)
+------------------------------+
| Stack | Heap | Data | Text   |
+------------------------------+↓ 页表映射
+------------------------------+
| 部分在物理RAM               |
| 部分在磁盘Swap              |
+------------------------------+
物理视角 (真实内存 + 磁盘)

十六、总结一句话

层次没有虚拟内存时有了虚拟内存后
内存访问程序直接操作物理地址程序操作虚拟地址,系统自动映射
程序隔离相互影响、容易崩溃地址空间隔离、安全
内存利用碎片化严重灵活调度、支持换页
程序设计复杂、需考虑实际地址简单、线性逻辑
http://www.dtcms.com/a/565909.html

相关文章:

  • DeviceNet转ProfiNet边缘计算网关赋能:西门子 1200PLC 与库卡机器人通讯配置完整案例
  • 网络卡顿运维排查方案:从客户端到服务器的全链路处理
  • 成都网站seo公司网站优化报告
  • 聊城网站制作价格做名片的网站
  • 辽宁网站建设招标网站如何做百度推广方案
  • ECharts 实战:`connectNulls` 的妙用——绘制连续折线图并跳过 0 值节点
  • Mysql引擎
  • 报表类系统后端API设计思路
  • 谷歌的技术栈是什么?
  • Token 存储与安全防护
  • HAProxy 简介及配置
  • 电商系统网站建设网站客户端制作教程
  • 只会后端不会前端如何做网站免费wordpress页面编辑器
  • BIRGMA验厂要求
  • 铝电解电容器用阳极箔:市场格局、技术演进与未来趋势
  • linux服务-vsftpd搭建
  • SAP PP生产报废单功能分享
  • 汇川H5U+HMI仿真运行追飞剪程序
  • 服装设计网站免费临桂住房和城乡建设局网站
  • 原子性与原子操作
  • Java使用okhttp发送get、post请求
  • 两种上传图片的方式——91张先生
  • web3品牌RWA资产自主发行设计方案
  • 网站公司是做什么的长沙做网站备案
  • 【k8s】Kubernetes 资源限制设置规范手册 MB与MiB的概念混淆问题
  • 网站开发需要多长时间互联网有限公司
  • 撰写网站规划书网络服务示范区创建情况
  • 汇川高压变频故障码解析F134 F149 F150 F151 F154 F155 F157 F159 F160
  • 从 C 到 C++20 协程编写方法的演变。第一部分:函数 + 宏 = 协程
  • 采购管理软件选型避坑指南