浅学内存布局(一)

用户态虚拟内存布局

1. 虚拟内存大小

不管程序是 用户态的程序 还是 内核态的程序,都需要使用虚拟地址,这个是因为计算机的硬件要求的(软件无条件服从硬件的规定),CPU需要经过"地址转换"得到最终的物理地址

2. 虚拟地址空间大小------32位操作系统

3. 虚拟地址空间大小------64位操作系统

TASK_SIZE = 0x0000 7FFF FFFF FFFF

4. 32位和64位系统用户态虚拟内存布局类似

内存映射区

Linux通过将一个 "虚拟内存区域" 与 一个 "文件对象" 关联起来,以初始化这个虚拟内存区域的内容,这个过程叫------内存映射(memory mapping)

文件对象:

  1. Linux文件系统中的普通文件
  2. 匿名文件,一块全部包含二进制零的物理内存

1.内存映射普通文件

具体一点的流程如下图:

  • 原理:将磁盘上的一个普通文件(如 data.txt)的全部或一部分映射到进程的虚拟地址空间。

  • 工作流程:

    • 当进程访问该内存区域时,如果数据不在物理内存中,CPU 会触发一个缺页中断。
    • 操作系统捕获这个中断,负责将文件对应的数据块(页)从磁盘加载到物理内存页中。
    • 然后更新进程的页表,建立虚拟地址到物理地址的映射。
    • 此后,进程对该内存的读写就相当于对文件的读写。
  • 写回时机:被修改的"脏页"会由操作系统内核线程在后台定期刷回磁盘,也可以由程序主动通过 msync() 强制立即写回。

2.内存映射匿名文件

内存映射匿名文件,说白了,我白说了,就是程序A,它特么想申请一块物理内存用用。

  • 原理:没有文件与之关联的映射。它映射的内存区域会被初始化为零。
  • 工作流程:
    • 操作系统分配新的物理内存页来满足映射需求。
    • 这些页不与任何文件关联,只用于进程间的共享或作为动态堆的替代分配方式(如 malloc 可能在某些场景下使用 mmap 来分配大块内存)。
  • 主要用途:
    • 进程间共享内存:fork() 后的父子进程可以通过匿名映射共享一块内存区域。
    • 分配大块内存:比传统的 malloc/brk 更高效,且分配的内存是页面对齐的。

内存映射的作用

1.共享对象

2.私有对象

3.动态共享库

内存映射的使用

POSIX 操作系统标准库(Linux, macOS, BSD 等), 系统调用。

POSIX 标准定义了一组用于内存映射的 API,主要在 sys/mman.h 头文件中。

arduino 复制代码
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
核心函数:创建映射

addr: 建议的起始地址(通常设为NULL由系统自动选择)。
length: 要映射的字节长度。
prot: 保护模式(PROT_READ,PROT_WRITE,PROT_EXEC的组合)。
flags: 映射类型(MAP_SHARED-修改写回文件, MAP_PRIVATE-写时复制, MAP_ANONYMOUS-匿名映射)。
fd: 文件描述符(匿名映射时设为-1)。
offset: 文件中的偏移量(通常为 0)。
返回值:成功返回映射区域的起始地址,失败返回MAP_FAILED。
arduino 复制代码
int munmap(void *addr, size_t length);
解除映射

addr:mmap返回的起始地址。
length: 要解除映射的区域长度(可以与映射时不同,但必须是页大小的整数倍)。对MAP_PRIVATE区域的修改会被丢弃; 对MAP_SHARED区域的修改会在解除前自动写回文件。
arduino 复制代码
int msync(void *addr, size_t length, int flags);
同步映射区与文件, 强制将内存中修改过的数据写回(刷盘)到文件。
flags:MS_ASYNC(异步写),MS_SYNC(同步写),MS_INVALIDATE(使缓存失效)。
arduino 复制代码
int mprotect(void *addr, size_t length, int prot);
修改映射区的保护模式,可以动态地将一段映射内存的权限从只读改为可写等。
arduino 复制代码
int madvise(void *addr, size_t length, int advice);
向内核提供访问建议,提示内核程序将如何访问该映射区域,以优化页面调度策略(如MADV_SEQUENTIAL提示会顺序访问,内核可提前预读)。

mmap系统调用小细节,了解即可。

虚拟内存区域的管理

Linux操作系统把进程的每个映射区抽象成一个 结构体:vm_area_struct 在内核中维护整个结构体简称VMA

arduino 复制代码
unsigned long vm_start; 区间起始地址 
unsigned long vm_end; 区间结束地址(不包含该地址)

struct vm_area_struct *vm_next, *vm_prev; 用于将所有 VMA 连接成一个有序的双向链表

unsigned long vm_flags; 该区域的一套行为标志 这是一组更宏观的属性,
   例如:VM_SHARED / VM_PRIVATE:共享映射还是私有映射。 
   共享映射 (MAP_SHARED):对映射区域的修改会反映到磁盘文件上(如果是文件映射),并且对其他映射了同一文件的进程可见。 
   私有映射 (MAP_PRIVATE):对映射区域的修改不会写回磁盘,也不会被其他进程看到(写时复制,Copy-on-Write)。
pgprot_t vm_page_prot; 该区域的页级保护权限。这是一个位掩码,定义了区域内页面的访问权限,
   如:PROT_READ (VM_READ):可读 
       PROT_WRITE (VM_WRITE):可写 
       PROT_EXEC (VM_EXEC):可执行

问题:一个虚拟地址,如何快速找到这个虚拟地址位于哪个vm_area_struct

链表用于维护相邻区域的关系,利于相邻区域的合并,红黑树用于高效查询指定区域

直接贴图答案:红黑树 ,大家AI了解

相关推荐
Java爱好狂.2 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
马良神笔4 小时前
2026 年Rag最近技术前沿总结
后端·python·django
程序猿DD4 小时前
同样10美元,OpenDesign和Command Code的DeepSeek V4.1 Flash额度到底有多少?
后端
高频因子挖掘机4 小时前
A股量化从日线升级到5分钟线:数据量增加后最先暴露哪些工程问题?
后端·github·api
高频因子挖掘机4 小时前
历史行情返回顺序变了,为什么按行号计算指标很危险?
后端·github·api
团子哒哒6 小时前
数组、切片和Map
后端
专注仿真6 小时前
ThinkPHP6 API接口开发实例:从零搭建RESTful风格接口
后端·php·restful
开心猴爷6 小时前
应对iOS审核4.3:可以使用 IpaGuard 来给属性名混淆的技术细节与平衡之道
后端·ios
1m555555556 小时前
RAG 工程实战(五):正确知识已经进入 Prompt,为什么大模型还是会答错?
后端·面试
易小尊6 小时前
我的模型在 65% 以上时准得可怕,在 50% 时像在掷硬币
后端