【知识讲解】 Linux虚拟地址空间认识


目录

前言

[Part1. 虚拟地址空间基础](#Part1. 虚拟地址空间基础)

[Part2. fork 与写时拷贝](#Part2. fork 与写时拷贝)

[Part2.1. fork子进程内部过程](#Part2.1. fork子进程内部过程)

[Part2.2. 写时拷贝](#Part2.2. 写时拷贝)

[Part2.3. 进程修改数据过程](#Part2.3. 进程修改数据过程)

[Part3. 缺页中断](#Part3. 缺页中断)

[Part4. 堆管理与malloc底层](#Part4. 堆管理与malloc底层)

[Part5. 虚拟地址的意义](#Part5. 虚拟地址的意义)

[Part6. 整体总结](#Part6. 整体总结)

[Part7. 结语](#Part7. 结语)


前言

在Linux操作系统中,认识虚拟地址空间是认识Linux内存管理非常重要的一部分,接下来来跟随小编的视角来看看吧。


let's go!!!!!!!!


Part1. 虚拟地址空间基础

一个进程对应一份虚拟地址空间。


• 32位机器:虚拟地址总空间 2^{32},大小4GB。

◦ 用户空间:0~3G;内核空间:3G~4G。

◦ 注意:内核空间,本质也是拿用户空间的虚拟地址去访问。

• 64位机器:拥有巨大的虚拟地址范围。


对于一个变量:

1. 变量拥有物理地址;同时,在所属进程的虚拟地址空间内,拥有虚拟地址。

2. 每个进程维护一张页表:虚拟地址 ↔ 物理地址,一一映射。

3. 小端机器取变量地址:取4字节中最小的地址;再结合类型做偏移,完成访问。


虚拟地址空间的内核数据结构

mm_struct 用来描述整个进程的虚拟地址空间。

内部包含若干个 vm_area_struct(VMA,虚拟内存区域),每一个代表一块区域:代码段、数据段、堆、栈。

每个VMA记录:start、end,代表该区域虚拟地址的起止;还有权限、偏移等属性。

虚拟地址空间是连续的;但是对应的物理内存可以是不连续。


Part2. fork 与写时拷贝

Part2.1. fork子进程内部过程

1. 调用fork(),分配内核数据结构,给子进程复制:task_struct、页表、虚拟地址空间。

2. 父进程的数据结构内容拷贝给子进程。

3. 将子进程加入运行队列。

4. fork返回,父子进程分别继续执行。

进程具备独立性:进程内部执行系统调用,都是操作自己这份虚拟内存。


Part2.2. 写时拷贝

fork刚创建子进程的时候:

1. 父子页表指向同一份物理内存,代码段、数据段共享物理页。

2. 但是把这些页的权限设置为只读。

3. 父子此时虚拟地址完全一样,映射到相同物理内存。


Part2.3. 进程修改数据过程

• CPU发现要写的页是只读,触发缺页异常。

• OS识别:这是写时拷贝场景,不是真的权限错误。

• OS分配一份新的物理内存,把原来页的内容复制过去。

• 修改当前进程的页表映射,让当前进程虚拟地址映射到新物理页;另一个进程依旧指向老物理页。

• 恢复页权限为可写,异常处理完毕,程序继续运行。

只有发生写操作,才真正分配物理内存;读操作依旧共享,不会拷贝。

fork 会返回两个id:父进程返回子进程pid,子进程返回0。本质是两个进程各自的页表,同一个虚拟地址读到不同返回值。


Part3. 缺页中断

虚拟地址已经分配,但还没有绑定物理内存,访问时触发缺页中断。


场景举例:我们程序有2G代码,虚拟地址会开辟2G虚拟空间,但是物理内存只有4G。不会一次性把全部代码加载进物理内存。

• 访问还没有映射物理内存的虚拟地址,触发缺页异常。

• 操作系统分配物理页,建立页表映射,把磁盘上对应的数据读到物理内存;异常处理完成,程序重新执行这条指令。


缺页的两种情况

1. 合法缺页:虚拟地址属于某个VMA(vm_area_struct),只是还没有分配物理页,正常分配内存即可。

2. 非法访问:访问的虚拟地址不在任何VMA之内,属于野指针,程序报段错误。


进程和内存解耦:进程的代码、数据,是基于虚拟地址。我们可以不释放虚拟地址,仅仅修改页表映射,解除和物理内存的绑定。不会影响进程运行。


进程退出

进程退出时:根据页表,把物理内存的数据写回磁盘;释放物理内存。


如果有虚拟内存,不需要把全部数据写回磁盘;只需要释放物理内存,虚拟地址本身依旧存在。


Part4. 堆管理与malloc底层

堆由vm_area_struct管理。


malloc(100)完整流程:

1. 在堆区划分虚拟地址,此时还没有物理内存。

2. 用户访问这块内存,触发缺页异常。

3. 操作系统分配物理内存,建立映射。

4. 如果堆空间不足,向OS申请更多虚拟地址。


free不会把虚拟地址还给操作系统。只是标记这块堆空间为空闲,交由malloc内部管理。堆过大才会触发堆回缩,把部分虚拟地址还给OS。


Part5. 虚拟地址的意义

1. 地址隔离保护:每个进程拥有独立虚拟地址空间,进程之间互不干扰。

2. 内存管理:虚拟地址连续,物理内存碎片化也没关系;OS可以把不常用内存置换到磁盘。

3. 权限管控:页表中设置页权限,实现只读、可读可写。访问违反权限直接触发异常。

4. 延迟分配物理内存:只有真正访问的时候,才分配物理内存。fork写时拷贝也是基于这个思想,节省内存。


Part6. 整体总结

1. 虚拟地址空间:逻辑上连续;物理内存可以离散。

2. 页表:完成虚拟地址→物理地址的翻译,同时记录页权限。

3. fork:初期父子共享物理页,标记只读;写时拷贝,发生写操作才复制物理页。

4. 缺页中断:虚拟地址已分配,无物理内存,访问触发异常;OS分配物理内存,加载数据。

5. malloc:先分配虚拟地址,真正访问才分配物理内存;free只做堆内部标记,不一定释放虚拟地址给操作系统。

6. VMA(vm_area_struct):管理各个段(代码段、数据段、堆、栈)的虚拟地址范围与权限。


Part7. 结语

这篇文章我们认识并知道了Linux虚拟地址空间,接下来,小编还会带来更多Linux相关的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

相关推荐
律宏阔6 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔6 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴6 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千6 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei376 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei376 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei376 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW6 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW6 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei376 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化