用户态虚拟内存布局
1. 虚拟内存大小

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


3. 虚拟地址空间大小------64位操作系统
TASK_SIZE = 0x0000 7FFF FFFF FFFF

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

内存映射区
Linux通过将一个 "虚拟内存区域" 与 一个 "文件对象" 关联起来,以初始化这个虚拟内存区域的内容,这个过程叫------内存映射(memory mapping)
文件对象:
- Linux文件系统中的普通文件
- 匿名文件,一块全部包含二进制零的物理内存
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了解
