浅学内存布局(一)

用户态虚拟内存布局

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

相关推荐
Cisyam8 分钟前
使用Bright Data API轻松构建LinkedIn职位数据采集系统
后端
float_六七9 分钟前
Spring Boot 3为何强制要求Java 17?
java·spring boot·后端
bobz96522 分钟前
ovs arp
后端
_風箏34 分钟前
SpringBoot【集成ElasticSearch 01】2种方式的高级客户端 RestHighLevelClient 使用(依赖+配置+客户端API测试源码
后端
用户214118326360240 分钟前
dify案例分享-零基础上手 Dify TTS 插件!从开发到部署免费文本转语音,测试 + 打包教程全有
后端
架构师沉默1 小时前
Java 开发者别忽略 return!这 11 种写法你写对了吗?
java·后端·架构
EndingCoder1 小时前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js
RainbowJie11 小时前
Gemini CLI 与 MCP 服务器:释放本地工具的强大潜力
java·服务器·spring boot·后端·python·单元测试·maven
ITMan彪叔1 小时前
Nodejs打包 Webpack 中 __dirname 的正确配置与行为解析
javascript·后端
用户89535603282202 小时前
告别重复,用Go泛型精简Gin代码
后端·gin