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

进来看看你对进程虚拟内存的了解有多深?

在 Linux 中每个进程都好像是楚门生活在一个别人为它精心构建的世界里而它却以为自己独占了整个系统的内存空间。这正是内核通过虚拟内存机制实现的。本文将带你穿过用户态的表象深入 Linux 内核源码与底层硬件分析这一套复杂的协作机制。在我们正式进入主题之前不妨先试着思考下面几个问题在一个父子进程中如果你同时打印一个全局变量的地址你会发现它们的逻辑地址也就是虚拟地址完全一样。但是当子进程修改这个变量时父进程的值却并没有改变。既然地址一样为什么在子进程中变了父进程中却没变如果你定义一个全局数组int arr[1024*1024] {1}已初始化编译出的可执行文件会多出 4MB。但如果你定义int arr[1024*1024]未初始化可执行文件的大小几乎没变。明明都是定义了 4MB 的数组为什么差别这么大在只有 8GB 物理内存的 Linux 系统上为什么你可以成功malloc出 100GB 的内存而不报错Linux 内核在管理进程的虚拟内存区域VMA时既然已经有了一个双向链表为什么还要大费周章地维护一棵红黑树在什么场景下这棵树会决定你的程序响应速度2018 年Linux 引入了KPTI 机制强制将用户态和内核态的页表分离。这导致了明显的性能下降内核开发者为什么要冒着性能大跌的风险非要在内存布局里建起这堵墙这些问题如果你能答出来那你很厉害了。如果某些地方还有些疑惑也请不要担心下面我会带大家深入了解这背后的底层原理。1. 进程虚拟内存的宏观布局在 Linux 中每个进程眼里的内存都是独占且连续的。但不同架构下进程眼里内存的大小和边界也完全不同。1.1 经典的32位系统在 32 位架构如 x86中地址空间共 2^324GB Linux 默认采用 3:1 分割用户空间 (0 ~ 3GB)进程自己折腾的地方。内核空间 (3GB ~ 4GB)所有进程共享用于存放内核代码、页表、物理内存映射区等。而由于内核空间仅 1GB当物理内存超过 1GB 时内核无法直接映射所有物理内存被迫引入了高端内存机制这极大地增加了内核开发的复杂性。下面介绍一下具体的细节在理想状态下内核希望通过直接映射来工作也就是把这 1GB 的内核虚拟地址直接映射在物理内存的最前面 1GB 上。如果物理内存只有 512MB那么内核这 1GB 的虚拟空间就绰绰有余每一寸物理内存都能在内核里找到相应的映射空间。如果物理内存有 4GB而内核只有 1GB 的虚拟地址。如果内核直接占用了这 1GB 虚拟地址它就只能看到前面 1GB 的物理内存剩下的 3GB 物理内存因为没有对应的虚拟地址映射内核根本无法直接访问。为了解决较小的虚拟内存映射较大的物理内存的问题内核引入了高端内存机制物理内存的前 896MB这部分是直接映射的内核随时可以访问效率极高而 896MB 之后的物理内存内核不给它们分配固定的虚拟地址。当内核需要访问这些 896MB 之后的物理内存时它会使用没有映射物理内存的剩下 128MB 虚拟内存临时建立一个映射指向目标物理内存用完后再拆掉。1.2 现代64位系统在 x86_64 架构下虽然地址线有 64 位但目前 Linux 仅使用了其中的 48 位部分新 CPU 支持 57 位。48 位的地址线提供了 256TB 的虚拟地址空间用户空间 (低 128TB)从 0x0000 0000 0000 0000 到 0x0000 7FFF FFFF F000。内核空间 (高 128TB)从 0xFFFF 8000 0000 0000 到 0xFFFF FFFF FFFF FFFF。由于只使用了 48 位地址线导致中间有一段巨大的、地址位不合法的区域如果程序尝试访问这里硬件会直接抛出通用保护异常。这种设计既精简了硬件电路也为未来扩展留下了空间。在内核空间如此庞大的情况下整块物理内存都会被映射到虚拟地址空间的某一个起始点内核访问任何物理地址都只需要简单的加法偏移。但是带来了便利的同时也付出了一些代价 由于映射范围极大页表本身占用的内存变多了内核现在普遍使用4 级甚至 5 级页表每次翻译地址的开销比 32 位2 级要高。此外所有的指针从 4 字节变成了 8 字节在处理大量包含指针的数据结构时内存占用会显著增加并且对L1/L2 缓存的压力更大因为缓存行能容纳的元素变少了。2. 用户空间详细布局我们从虚拟地址的最低端一路向上扫过看看一个运行中的程序到底把东西都藏在哪了。1.代码段(.text)存放编译后的机器指令。只读、可执行这是为了保护程序不被意外篡改。多个进程运行同一个程序时物理内存中实际只存一份代码。2.常量区 (.rodata)存放const修饰的全局变量、字符串常量。只读尝试修改这里会导致段错误。3.已初始化数据段 (.data)明确赋了初值且初值不为 0 的全局变量和静态变量。可读写它们在程序启动瞬间就有了初始值。4.未初始化数据段 (.bss)未初始化或初值为 0 的全局或静态变量。不占磁盘空间在可执行文件中仅记录一个大小当程序加载到内存时内核会分配内存并将其全部清零。这也是为什么全局变量默认值是 0 的底层原因。5.堆由低地址向高地址生长。6.内存映射区 (mmap)动态链接库、大块内存分配malloc 超过 128KB 时、文件映射。现代 Linux 中通常从高地址向低地址生长紧贴栈底下方。7.栈存放局部变量、函数参数、返回地址。由高地址向低地址生长底部通常设有 Guard Page 保护页一旦触碰即触发溢出报错。8.命令行参数与环境变量用户空间的最高端。存储main(int argc, char argv, char envp)中的参数和系统环境变量。它们由父进程通常是 Shell在execve时压入。3. 内核如何管理这些区域3.1 mm_struct每个进程都有一个task_struct进程控制块而每个进程控制块中都有一个mm指针指向mm_struct它是内存管理的核心数据结构描述了一个进程所拥有的全部虚拟内存视图。所有用户线程共享同一个mm_struct而内核线程mm指针为 NULL因为内核线程没有用户空间它们直接借用上一个进程的内核页表。我们可以把mm_struct的内容分为四大块1.内存映射区域这是最核心的部分进程的虚拟地址空间不是连续的而是由许多离散的块组成的如代码段、数据段、堆、栈、mmap区。mmap指向vm_area_struct的链表按虚拟地址排序方便遍历。mm_rb指向红黑树的根节点红黑树用于快速查找某个地址属于哪个区域也就是寻找 VMA。2.内存段的起止位置start_codeend_code: 可执行代码段的范围。start_dataend_data: 已初始化数据的范围。start_brkbrk: 堆的起点和当前的终点。start_stack: 栈的起点。3.页表指针pgd: 指向当前进程的一级页表也就是全局页目录。当进程切换时内核会把这个pgd的物理地址加载到 CPU 的 CR3 寄存器中。CPU 的 MMU 就能根据这套页表将虚拟地址翻译成物理地址。4.状态统计total_vm: 进程总的虚拟内存大小。rss: 进程当前实际占用的物理内存大小。mm_users/mm_count: 引用计数多个线程共享同一个mm_struct时计数会增加。可能有人会产生这样的疑惑既然有了页表为什么还要mm_struct下面的 VMA 链表其实页表是给内存管理单元 MMU 看的它只负责告诉 CPU地址 A 对应物理地址 B权限是只读。而 VMA 是给内核看的当程序发生缺页中断时内核需要通过mm_struct查表这个地址合法吗如果不合法报段错误。如果合法是因为还没分配物理内存吗如果是则分配一个物理页并填入页表。3.2 为什么有了链表还需要红黑树这其实是 Linux 内核设计中的经典权衡1.双向链表按地址顺序排列方便内核遍历所有的内存区域。比如当你运行cat /proc/pid/maps时内核就是沿着链表走一遍把信息打印出来。2.红黑树当 CPU 访问一个地址时内核需要以极快的速度判断这个地址是否合法并查看这个地址属于哪个 VMA。如果只用链表查找复杂度是O(N)如果进程映射了成千上万个动态库查找会非常慢。而红黑树将查找复杂度降到了O(logN)无论是缺页异常还是内存保护检查红黑树都决定了系统的响应效率。4. 问题解析现在我们来分析一下开头的问题。4.1 延迟分配当你调用malloc分配内存时内核其实是非常聪明的。它并不会立刻跑到物理内存条上去给你占坑而是在mm_struct里加一个 VMA 记录然后告诉你这块内存已经是你的了。这就是为什么你能在 8GB 的机器上申请 100GB 的虚拟内存前提是系统开启了 Overcommit。只有当你真正开始读写这块内存时硬件会发现对应的页表项是空的触发一个缺页异常。这时内核才去分配物理内存并更新页表。4.2 BSS段与已初始化数据段的区别已初始化数据段.data里面存的是具体的初值。这些值必须实实在在地写进磁盘的可执行文件里加载时原样搬进内存。BSS段.bss里面全是初始为 0 的变量。内核只需要记住这里有 4MB 的空间需要清零即可没必要在磁盘上存 4MB 的零。程序启动时内核直接分配一批零页给它既省磁盘又省 IO。4.3 写时复制这是回答开头关于父子进程地址相同但值不同的关键。在 Linux 下fork()产生子进程时并不会复制一份物理内存。相反它让子进程的页表直接指向父进程的物理页并把这些页的权限全部设为只读。对于读操作父子进程共享同一块物理内存相安无事。而对于写操作当子进程尝试修改变量时硬件触发异常。内核查看之后得知这是写时复制页。于是内核会为子进程 **拷贝 **一份物理页再让子进程的页表指向这个新页面然后加上写权限。因此虽然虚拟地址是一样的但背后映射的物理地址已经在你不知不觉中改变了。5. 多级页表与 MMU要理解现代内存管理的精髓必须把MMU和多级页表放在一起看。5.1 页表5.1.1 为什么需要页表假设物理内存是 4GB页面大小为 4KB那么总共有 1M 个页框。如果使用单级页表为了映射这 4GB 空间每个进程都需要一个包含 1M 个表项的数组。如果每个表项占 4B那每个进程光页表就要占用 4MB 内存。此外单级页表要求物理上连续即使中间大片空间没用到你也得为这些空洞预留页表项这太浪费了。5.1.2 多级页表多级页表的核心思想是只有当某个区域真的存了数据才为它建立下一级页表。以 32 位系统两级页表为例一级页表 (PGD/页目录) 将 4GB 分成 1024 份每份 4MB。二级页表 (PTE/页表项) 只有当进程真的访问了某个 4MB 范围时内核才会创建一个二级页表。5.2 MMUMMU是 CPU 内部的一个硬件单元它的唯一任务就是把虚拟地址转换为物理地址。以 64 位 4 级页表为例CPU 从控制寄存器如 x86 的 CR3中读取当前进程第一级页表PGD的物理基地址。逐级拆解地址MMU 会把 64 位的虚拟地址拆成好几段。9bit 索引第一级页表找到第二级地址以此类推。最后一级页表取出物理页的基地址加上虚拟地址末尾的 12bit偏移量得到最终的物理内存地址。5.3 TLB快表由于多级页表需要多次访问内存4 级页表意味着翻译一个地址要查 4 次内存这比 CPU 执行指令慢得多。为了提速MMU 内部有一个超高速缓存TLB。它缓存了最近翻译过的虚拟页到物理页的映射关系。现代系统 TLB 命中率通常在 99% 以上翻译几乎是瞬时的。但是当进程切换也就是切换mm_struct时由于页表变了旧的 TLB 缓存通常必须失效这就是进程切换开销大的原因之一。6. 安全与性能的博弈最后我们聊聊第五个问题。有些系统调用比如gettimeofday频率极高。如果每次都切入内核态上下文切换的开销很大。所以内核会在用户空间最高端映射一个 vDSO 区域里面放的是内核提供的只读代码用户态直接调用无需切换模式。2018 年由于 Intel CPU 的熔断漏洞黑客可以通过预测执行在用户态窃取内核内存的信息。以前用户态和内核态共用一套页表只是权限位不同。现在内核被迫做了隔离用户态运行时页表里几乎不包含内核地址。因此现在每次从用户态进入内核态都要切换页表性能损耗由此而来。但为了安全我们不得不接受这个改变。写在最后:进程虚拟内存不只是为了给进程提供独占空间它是 Linux 内核在效率、性能与安全之间不断权衡的杰作,理解了虚拟内存才算是真正触碰到了操作系统的灵魂。当你下次看到Segmentation Fault时希望你脑海中的认知将不再只是一个简单的报错而是整个 VMA 红黑树在警告“你越界了那是我不曾承诺给你的世界。”本文结束。

相关文章:

进来看看你对进程虚拟内存的了解有多深?

在 Linux 中,每个进程都好像是楚门,生活在一个别人为它精心构建的世界里,而它却以为自己独占了整个系统的内存空间。这正是内核通过虚拟内存机制实现的。 本文将带你穿过用户态的表象,深入 Linux 内核源码与底层硬件,分…...

分布式驱动汽车稳定性控制:分层式直接横摆力矩控制策略

分布式驱动汽车稳定性控制。 采用分层式直接横摆力矩控制,上层滑模控制,下层基于轮胎滑移率最优分配。 滑模控制跟踪横摆角速度和质心侧偏角误差。 七自由度整车模型输出实际质心侧偏角和横摆角速度,二自由度模型输出理想质心侧偏角和横摆角速…...

Python基于flask的在线学习考试组卷管理系统 带前台-可视化

目录系统架构设计核心功能模块智能组卷系统考试过程管理可视化数据分析技术实现路线部署方案项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作系统架构设计 采用前后端分离模式,前端使用HTM…...

Pandownload:百度网盘下载体验的悄然升级

百度网盘就像一个可靠的伙伴,帮我们存放海量照片、视频、文档和学习资料。它的分享链接一键传输、跨平台同步这些优点,让无数人离不开它。可一到下载环节,许多人就忍不住叹气:为什么明明资源就在那里,速度却慢得像老牛…...

新能源锂电涂布机应用案例:多轴伺服控制下的1500博图程序设计实践与西门子Basic触摸屏操作...

新能源锂电涂布机博图1500程序案例,有西门子Basic触摸屏案例同步,设计到多轴伺服运动控制,变频器控制,张力计算等。新能源锂电涂布产线的自动化控制,说白了就是多轴伺服、变频器、张力算法的协同作战。最近刚搞完一个用…...

Agent-Browser 简明教程

您的AI代理需要在网站上填写表单。使用传统的浏览器自动化工具,这个简单的任务仅为了描述页面结构就会消耗超过15,000个token。当您浏览三个页面时,上下文窗口会以比您使用它们更快的速度消耗token。 Agent-browser 来自 Vercel Labs 用根本不同的方法解…...

PID算法(3)- PID就是求【针对控制量的一个比例+积分+微分】的值

举例说明:通过PID算法实现电机转速的控制,控制量(控制目标)是电机的转速对电机转速的控制手段是PWM的占空,如下图所示:那么PID控制器的机制就是,将转速转化为PWM的占空比,而PID控制器经过PID算法&#xff0…...

勒索病毒专盯数据库? TDE 透明加密如何筑起“最后一道防线

标签:#TDE #勒索病毒防护 #数据库安全 #透明加密 #安当 #等保三级一、真实事件:勒索病毒没加密文件,却锁死了数据库 2025 年 10 月,我司一台部署在内网的 SQL Server 数据库服务器因员工点击钓鱼邮件,感染 LockBit 3.0…...

李哥深度学习班学习笔记——图像识别

一、导入依赖库​ import random #用于设置随机种子,保证实验可复现 import torch #Pytorh核心库,构建和训练神经网络 import torch.nn as nn #Pytorch神经网络层模块 import numpy as np #数值计算库,处理矩阵 import os #…...

Spring AI Alibaba学习记录(ChatModels篇)

目标:实现简单的聊天接口调用依赖配置(Maven)添加以下依赖:​ <dependencies> <!-- Spring AI Alibaba Agent Framework --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-agent-framework</arti…...

SQL 客户端远程登录服务器详细操作教程

在日常开发和运维工作中&#xff0c;经常需要通过 SQL 客户端远程连接服务器上的 SQL Server 数据库&#xff0c;本文将详细讲解完整的配置和连接步骤&#xff0c;同时附上连接失败的排查方法&#xff0c;亲测有效&#xff0c;适合新手快速上手。一、服务器端 SQL Server 配置远…...

【备赛指南】2026全国大学生嵌入式大赛-ST赛道官方推荐,华清远见STM32U5/MP157开发板助你一臂之力!

第九届&#xff08;2026&#xff09;全国大学生嵌入式芯片与系统设计竞赛&#xff08;以下简称"大赛"&#xff09;已正式启动&#xff0c;报名火热进行中。作为教育部认证的国家级A类赛事&#xff0c;大赛历经九年积淀&#xff0c;已成为检验高校电子类专业人才培养质…...

vmd分解联合小波阈值降噪MATLAB代码。具体实现功能如下: 1.数据加载与预处理 数据从CSV文件读取并转换为数组,处理了多列数据的情况。 采样频率 Fs 设置为1000 Hz,这是后续时频分析的

vmd分解联合小波阈值降噪MATLAB代码。具体实现功能如下&#xff1a; 1.数据加载与预处理 数据从CSV文件读取并转换为数组&#xff0c;处理了多列数据的情况。 采样频率 Fs 设置为1000 Hz&#xff0c;这是后续时频分析的基础。 2.参数初始化 VMD分解参数&#xff08;如带宽约束 …...

分库分表(一)

假设目前有一张表order 订单表&#xff0c;需要分库分表&#xff0c;举例给出方案设计&#xff0c;并解决上述提到的分库分表带来的问题&#xff0c;详细说明下面给你用一个比较接近真实互联网生产环境的例子来说明&#xff1a; 假设有一张 order 订单表&#xff0c;数据量增长…...

京东社招——Java后端开发面试复盘

最近参加了京东大数据的面试&#xff0c;整个过程涵盖了项目细节、基础八股文以及手撕算法。本文将这次面试中遇到的9个核心问题进行了复盘和深度整理&#xff0c;特别是关于RabbitMQ的消息积压处理以及MySQL的RR隔离级别实现。base&#xff1a;北京1. 项目问题&#xff1a;你的…...

全自动颗粒清洁度分析系统,西恩士工业让颗粒计数精准高效

颗粒计数是清洁度检测的核心环节&#xff0c;传统的人工颗粒计数&#xff0c;不仅效率低&#xff0c;还容易出现误判、漏判&#xff0c;尤其是大规模生产时&#xff0c;人工计数根本跟不上节奏。西恩士的全自动颗粒清洁度分析系统&#xff0c;让颗粒计数摆脱人工&#xff0c;做…...

为什么要使用动态IP代理?详解动态IP在不同业务场景下的技术选型逻辑

做网络爬虫总被封IP&#xff1f;多账号运营动辄被风控警告&#xff1f;采集数据时地域限制拦路、数据不准&#xff1f;相信很多做技术、做运营的伙伴&#xff0c;在高频网络操作中&#xff0c;都曾被这些问题困住。为什么很多企业和从业者&#xff0c;都离不开动态IP代理&#…...

交稿前一晚!千笔,专科生论文救星!

你是否曾在论文写作中感到力不从心&#xff1f;选题无头绪、框架混乱、文献资料难找、查重率高得让人焦虑……这些难题&#xff0c;是否让你夜不能寐&#xff1f;专科生的论文之路&#xff0c;本就充满挑战&#xff0c;而千笔AI&#xff0c;正是为解决这些问题而生。它用智能技…...

实测有效:解决VSCode编译运行C++前无故卡顿的方案

最近用VSCode写C&#xff08;其实C也一样&#xff09;的时候发现了一个很闹心的问题&#xff1a;编译和运行本身速度没问题&#xff0c;但每次点执行后&#xff0c;VSCode会先卡好几秒才正式开始编译&#xff0c;加载圈完全是空转&#xff0c;白白浪费时间。踩了不少坑后终于找…...

求最大子序和---涉及到贪心+动态规划

1.什么是贪心和动态规划package siyangyuan;/*** Class Name :MaxSubArray* Package :siyangyuan* Description:** Author: Mr.chunxugao* Create: 2026-03-12- 14:13* Version:v1.0*///求最大子序和 public class MaxSubArray {public static int maxSubArray(int[] nums){//贪…...

2026全球范围内最知名且权威的计算机科技与人工智能信息获取平台

以下是全球范围内最知名且权威的计算机科技与人工智能信息获取平台&#xff0c;涵盖科技媒体、学术研究、开发者社区、AI工具导航以及趋势追踪平台等多个维度。一、国际权威科技媒体TechCrunch网址&#xff1a;https://techcrunch.com/硅谷科技风向标&#xff0c;长期关注科技创…...

基于PLC的药品包装机控制系统设计

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…...

基于PLC的小型音乐喷泉设计

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…...

如何从bode图得到单位阶跃的趋势

传递函数>阶跃响应&#xff1b;拉普拉斯逆变化就可以得到时域函数&#xff0c;可以绘制出图像。bode图>阶跃响应&#xff1b;以下例子进行测试&#xff1a;Ge -15.93 s---------------------s^2 16.34 s 94.09从伯德图&#xff08;Bode Plot&#xff09;定性判断单位阶…...

基于PLC的隧道照明控制系统设计

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…...

期货软件开发「启动加载页 / 初始化窗口」

一、初始化窗口作用&#xff1a;• 程序刚打开时显示• 后台连接行情服务器• 加载历史 K 线 / 行情数据• 加载完成后自动进入主界面 二、当前已实现的核心功能1. 窗体基础功能• 无边框拖动&#xff08;顶部栏按住可移动窗口&#xff09;• 最小化按钮 •关闭按钮• 基于 …...

供应链产研交付提效:后端开发提效实战

引言 在传统后端开发流程中&#xff0c;需求解析、接口开发、测试验证、部署上线等环节存在大量重复性工作&#xff0c;且需求变更易导致研发成本上升、交付周期延长。为解决这一痛点&#xff0c;本文围绕后端开发提效展开全维度规划&#xff0c;从架构设计、分阶段落地计划、核…...

洛谷P8218 【深进1.例1】求区间和 考点:一维前缀和

题目 P8218 【深进1.例1】求区间和 - 洛谷 核心思路 一维前缀和模板题 代码 可以让AI总结一下代码逻辑 这段代码实现了一维前缀和算法&#xff0c;用于高效解决区间求和问题。主要逻辑如下&#xff1a; 预处理 (数据输入与求和)&#xff1a; 读取数组长度 n。在读取每个元…...

SGP.22 eSIM通信原理-打电话

三个关键阶段&#xff1a;准备阶段&#xff08;下载Profile&#xff09;、激活阶段&#xff08;网络注册&#xff09;和使用阶段&#xff08;拨打电话&#xff09;。 第一阶段&#xff1a;准备——将中国移动的“身份”下载到手机 用户买了一部支持eSIM的新手机&#xff0c;决定…...

Python自动化实现思路

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快Python自动化实现思路通常分为以下几步1. 确定自动化测试的范围和目标&#xff1a;首先需要明确需要进行自动化测试的范围和目标&#xff0c;包括测试场景、测试用…...