浅学内存布局(一)

用户态虚拟内存布局

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了解

相关推荐
烈风1 天前
004 Rust控制台打印输出
开发语言·后端·rust
用户21411832636021 天前
用 AI 一键搞定!中医药科普短视频制作升级版
后端
秋难降1 天前
零基础学习SQL(十一):SQL 索引结构|从 B+Tree 到 Hash,面试常问的 “为啥选 B+Tree” 有答案了
数据库·后端·mysql
SamDeepThinking1 天前
用设计模式重构核心业务代码的一次实战
java·后端·设计模式
用户49055816081251 天前
lvs会话同步
后端
用户49055816081251 天前
linux内核网络协议栈报文的处理过程
后端
夜宵饽饽1 天前
上下文工程实践 - 工具管理(上篇)
javascript·后端
ERP老兵_冷溪虎山1 天前
Python/JS/Go/Java同步学习(第十三篇)四语言“字符串转码解码“对照表: 财务“小南“纸式转码术处理凭证乱码崩溃(附源码/截图/参数表/避坑指南)
java·后端·python
努力的小郑1 天前
MySQL索引(四):深入剖析索引失效的原因与优化方案
后端·mysql·性能优化