8/28 深入理解计算机系统笔记 内存映射
9.8 内存映射
定义:将一个虚拟内存区域和一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容的过程被称为内存映射。
虚拟内存区域可以映射到下面两种类型的对象中的一个:
- Linux文件系统中的普通文件:因为按需进行页面调度,所以这些虚拟页面没有实际交换进入物理内存,而是等到CPU第一次引用这个页面(发射一个落在这个页面范围内的虚拟地址)。如果区域比文件大小大,则用零填充剩下区域。
- 匿名文件:匿名文件由内核创建,包含的全是二进制的零。CPU第一次引用这个区域内的虚拟页面之后,内核就换出一个牺牲页面,并用二进制0覆盖并更新页表,然后把这个页面标记为驻留在内存中。注意:磁盘和内存没有实际的数据传输,所以映射到匿名文件中的页面也叫请求二进制零的页。
进程的共享区域和私有区域。
私有对象使用一种叫做写时复制的技术,私有对象开始生命周期的方式基本与共享对象一样,在物理内存中只保存一份私有对象的副本。
每个映射私有对象的进程,相应私有区域的页表条目被标记为只读。区域结构被标记为私有的写时复制,只要没有进程试图写自己的私有区域,它们就继续共享一份物理内存的副本。如果一个进程试图写私有区域的某个页面,就会触发一个保护故障。
故障处理程序会在物理内存中创建这个页面的一个新副本,并更新页表指向这个新副本,然后恢复这个页面的可写权限。
当故障处理程序返回时,CPU重新执行这个写操作,之后在这个新页面上的写操作就可以正常运行了。
注意:是以页面为单位。
图片见书P584
fork函数
fork函数被当前进程调用时,内核给新进程创建各种数据结构,并分配一个唯一的PID,为了给创建新的虚拟内存,创建了当前进程的mm_strct、区域结构和页表的副本。并将两个进程的每个页面都标记为只读,并将两个进程中每个区域结构都标记为私有的写时复制。
execve函数
加载和执行程序,加载a.out步骤如下:
execve("a.out", NULL, NULL);
- 删除已存在的用户区域,删除当前进程虚拟地址的用户部分中已经存在的区域结构。
- 映射私有区域,为新程序的代码数据、bss和栈区域建立新的区域结构。这些区域都是私有的写时复制。代码和数据区域都被映射在a.out文件的.text区和.data区。bss区是请求二进制零的,映射到匿名文件,栈和堆也是请求二进制零的,初始长度为0.
- 设置程序计数器(PC)。设置程序计数器,使之指向代码区域的入口。
mmap函数
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
mmap函数要求内核创建一个新的虚拟内存区域,并将文件描述符fd指定的对象的一个连续的片映射到这个新的区域。最好是从地址start开始的一个区域(只是暗示,一般设置为NULL)。映射的长度为length,从距文件开始处偏移量为offset字节的地方开始。
prot参数包含描述新映射的虚拟内存区域的访问权限位(vm_prot)
PROT_EXEC: 这个区域的页面由可以被CPU执行的指令组成。
PROT_READ: 这个区域可读
PROT_WRITE: 这个区域可写
PROT_NONE: 这个区域不能被访问
flags参数由描述被映射对象类型的位组成,设置了MAP_ANONYMOUS标记位,那映射的对象就是一个匿名对象,MAP_PRIVATE的对象就是一个私有的写时复制的对象,而MAP_SHARED表示是一个共享的对象。(父子进程共享映射区)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用