CSAPP学习笔记——虚拟内存(二)

news2024/9/24 3:27:22

案例研究

Intel Core i7

该处理底层的Haswell微体系结构允许64位的虚拟和物理地址空间,而现在的Core i7实现支持48位(256TB)虚拟地址空间和52位(4PB)物理地址空间,这对目前来说已经完全够用了。(Linux的虚拟内存系统中页的大小为4KB)

  1. 介绍了Core i7内存系统的重要部分——处理器封装

    image-20230218204056739
  2. 介绍了Core i7的地址翻译情况

    • 层级结构的TLB

    • 层级结构的页表(4层),每一层页表占VPN的9位,也就是有 2 9 2^9 29个PTE

    • 层次结构的高速缓存L1、L2和L3

    • 其中CR3控制寄存器指向第一级页表的起始位置,CR3的值是每个进程上下文的一部分,每次上下文切换的时候,CR3的值都会被恢复

      image-20230218204617440
  3. 介绍了页表项PTE的格式

    • 每个PTE占8个字节64位
    • 其中有40位是作为指向下一级页表物理基地址(这个地方我有个疑问?在这个例子里每一级页表的每一个PTE都有40位用来下一级页表的基地址,这难道不会很浪费吗?只存一次不就好了吗?)
    • 还有其他位用于权限控制和协助替换算法
    image-20230218205300593

Linux虚拟内存系统

  1. Linux虚拟内存区域

    Linux将虚拟内存组织成一些区域的集合。一个区域就是已经存在着的(已分配的——已缓存和未缓存)虚拟内存的连续片,也就是说Linux虚拟内存系统所定义的区域范围是比虚拟页大的,虚拟页是区域的基本单元。这些页是以某种方式相关联的,例如代码段、数据段、堆、共享库段和用户栈都是不同的区域。

    每个存在的虚拟页面都保存在某个区域,而不属于某个区域的虚拟页是不存在的(虚拟页本就是为进程所服务),并且不能被进程引用。内核不用记录这些不存在的虚拟页,能够节省更多的资源

    下图是一个Linux进程等虚拟内存

    image-20230218211727163
  2. Linux如何组织虚拟内存

    内核为每一个进程都单独维护一个task_struct,task_struct中的元素包含或者指向内核运行该进程所需的所有信息(例如PID、指向用户栈的指针%rsp,可执行目标文件的名字,程序计数器PC等)

    image-20230218211815499

    task_struct中一个条目指向mm_struct,它描述了虚拟内存的当前状态。我们感兴趣的字段有两个,pgd和mmap:

    • pgd指向第一级页表的基址,当内核运行这个进程时就把pgd的值放进CR3寄存器中

    • mmap指向一个vm_area_struct,每一个vm_area_struct都描述了当前虚拟地址空间的一个区域

      vm_area_struct的结构如下:

      • vm_start:指向这个区域的起始处
      • vm_end:指向这个区域的结束处
      • vm_prot:描述这个区域内所有页的读写权限
      • vm_flags:描述这个区域内的页面是与其他进程共享的还是私有的
      • vm_next:指向链表的下一个vm_area_struct
  3. Linux缺页异常处理

    当出现缺页异常,控制将转移到内核的缺页处理程序,处理程序随后执行如下步骤:

    1. 虚拟地址A时合法的吗?即地址A是否在某个vm_area_struct指向的区域内。缺页处理程序通过搜索vm_area_struct的链表,把A和每个vm_area_struct的vm_start和vm_end进行对比来得到结果。如果虚拟地址A不是合法的,就会抛出段错误

      因为一个进程可以创建任意个vm_area_struct,如果通过链表来搜索会造成大量时间损耗,因此在实际中,Linux利用我们看不见的字段建立了一个树,并在这棵树上查看

    2. 试图进行的内存访问是否合法?换句话说进程是否有读写或者执行这个区域内页面的权限?例如,如果缺页是由于一条对代码段中的只读页面进程写操作造成的,处理程序就会抛出段错误

    3. 此刻,内核已经知道这个缺页是由于对合法的虚拟地址进行合法访问造成的。那么处理程序会选择一个牺牲页面,如果牺牲页面被修改了,那么就将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令

内存映射

我们好奇虚拟内存是如何与磁盘上的对象关联起来的。Linux上通过一个叫内存映射的过程来实现虚拟内存的初始化,内存映射有两种形式:

  1. **Linux文件系统中的普通文件:**一个区域可以映射到一个普通磁盘文件的连续部分,例如一个可执行文件。文件区被分成页大小(4KB)的片,每一片包含一个虚拟页面的初始内容。因为虚拟页面是按需调度的(已缓存或未缓存),所以这些虚拟页面并没有实际进入物理内存(DRAM),直到CPU第一次引用到页面,即发射一个虚拟地址,触发访问合法地址的合法访问的缺页。

    一个区域的大小如果比文件区要大,那么剩下的就用0填充

  2. 匿名文件:一个区域也可以映射到匿名文件。匿名文件是由内核创建的,包含得全是二进制零。CPU第一次引用这种页面时,内核会在虚拟内存中找到合适的替换页面,如果该页面有修改,那么就换出去,用二进制零覆盖页面并更新页表。注意在这个过程中,磁盘和内存之间没有实际的数据传输,因此映射到匿名文件的区域中的页面也叫请求二进制零的页,通常出现在可执行文件的.bss段以及栈和堆,初始长度都是0,特点都是没有和磁盘的数据交互所以就用匿名文件映射。

再看共享对象

内存映射为我们提供了一种清晰的基址,用来控制多个进程如何共享对象

一个对象被映射到虚拟内存的一个区域,可以是共享对象也可以是私有对象,一个进程对一个共享对象的任何写操作其他进程也都会看见,并且会作用在磁盘的原始对象上。

image-20230219201013031

私有对象使用的是一种写时复制的技术映射到虚拟内存中,一个私有对象开始生命周期的方式与共享对象一致,在物理内存中只保存有私有对象的一份副本,其中两个进程将一个私有对象映射到它们虚拟内存的不同区域,但是共享这个对象的同一个副本。对于每个映射私有对象的进程,相应私有区域的PTE是标记为只读的,并且vm_area_struct标记为私有的写时复制

只要进程没有试图去写它的私有区域,它们就可以继续共享物理内存中对象的一个单独副本,然而只要有一个进程试图写私有区域的某个页面,那么这个写操作就会触发一个保护故障

当故障处理程序发现是由于进程试图写私有区域中一个页面而引发的时,它会在物理内存中新建这个页面的新副本(注意只是页面的副本而不是整个对象),更新当前进程对应页面的PTE指向该新副本(其他进程的页表仍然指向旧的页面),然后将页表的权限改为可写,处理完后控制传会引发故障的指令

image-20230219201809930

再看fork函数

之前的知识已经告诉我们,父进程和fork的子进程拥有的是两个互不干涉的地址空间

当fork函数被父进程调用时,内核就为子进程创建各种数据结构,并分配了唯一的PID(task_struct),然后为了给这个进程创建虚拟内存,它创建了父进程的mm_struct、vm_area_struct和页表的原样副本给子进程的地址空间(因为父进程和子进程的代码和数据都有一模一样的,区别在于调用fork函数后跳转的部分不同)。并且将两个进程的每个页面都标记为只读,每个区域结构都标记为私有的写时复制

这样fork函数在子进程中返回时(即将跳转到与父进程不同的代码段),父进程现在的虚拟内存(调用fork函数时)和子进程的虚拟内存是一样的。当这两个进程中任意一个,有写操作时,写时复制机制都会创建新页面,彼此互不影响

再看execve函数

假设在当前进程中执行了如下的execve调用

execve("a.out", NULL, NULL);

execve函数在当前进程加载并运行a.out中的程序,用a.out程序替代当前程序,步骤如下:

  1. 删除已存在的用户区域
  2. 映射私有区域:为新程序的代码、数据、bss和栈区域创建新的vm_area_struct,所有的这些区域都是私有的,写时复制的
  3. 映射共享区域
  4. 设置程序计数器:execve的最后一件事就是设置当前程序上下文中的程序计数器,使之指向新代码区域的入口点
image-20230219203601812

使用mmap函数的用户级内存映射

前面提到过的,一个进程可以创建大量的虚拟内存区域,怎么创建呢?Linux进程可以使用mmap函数来创建新的虚拟内存区域,并将对象映射到这些区域

#include <unistd.h>
#include <sys/mman.h>

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
// 如果成功则返回指向映射区域的指针,如出错则为MAP_FAILED(-1)

mmap函数要求内核创建一个新的虚拟内存区域,最好是从地址start开始的一个区域,并将文件描述符fd指定的对象的一个连续的片映射到这个新区域。连续的片的大小为length字节,从距文件开始处偏移量offset字节的地方开始。

start只是一个暗示,通常设置为NULL

prot包含新映射的虚拟区域的访问权限位(vm_area_struct里的vm_prot)

flags由描述被映射对象类型的位组成,如果设置了MAP_ANON标记位,那么映射的对象就是一个匿名对象,那么相应的虚拟页面就是请求二进制零的。相应的还有MAP_PRIVATE和MAP_SHARED

例如

bufp = mmap(NULL, size, PROT_READ, MAP_PRIVATE|MAP_ANON, 0, 0);

该函数让内核创建了一个size字节的,只读的,私有的,请求二进制零的虚拟内存区域,如果调用成功,bufp包含新区域的地址

mmap函数可以删除虚拟内存的区域

int mmap(void *start, size_t length);

删除从虚拟地址start开始的长度为length字节的区域

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Liunx(狂神课堂笔记)

一.常用命令 1. cd 切换目录 cd ./* 当前目录cd /* 绝对路径cd .. 返回上一级目录cd ~ 回到当前目录pwd …

定点数的表示和运算

文章目录真值&#xff08;有正负号&#xff09;和机器数&#xff08;0正1负&#xff09;原码整数小数补码负数的补数正数的补数[y]~补~ > [-y]~补~反码小结移码移位运算加减法运算溢出判断真值&#xff08;有正负号&#xff09;和机器数&#xff08;0正1负&#xff09; 无符…

链表OJ(六)链表相加(一) 链表相加(二)

目录 链表相加(一) 链表相加(二) 描述 二与一相比多了俩次反转而已 链表相加(一) 描述 给定两个非空链表逆序存储的的非负整数&#xff0c;每个节点只存储一位数组。 请你把两个链表相加以下相同方法返回链表&#xff0c;保证两个数都不会以 0 开头。 【我的解法】长到…

实例五:MATLAB APP design-APP登录界面的设计

一、APP 界面设计展示 注:在账号和密码提示框输入相应的账号和密码后,点击登录按钮,即可跳转到程序中设计的工作界面。 二、APP设计界面运行结果展示

使用继承的虚函数表

​ 代码 #include <iostream> using namespace std;class Father { public:virtual void func1() { cout << "Father::func1" << endl; }virtual void func2() { cout << "Father::func2" << endl; }virtual void func3()…

一文彻底理解大小端和位域 BIGENDIAN LITTLEENDIAN

一文彻底理解大小端和位域 为什么有大小端 人们一直认为大道至简&#xff0c;就好像物理学上的世界追求使用一个理论来统一所有的现象。为什么cpu存在大小端之分&#xff0c;一言以蔽之&#xff0c;这两种模式各有各的优点&#xff0c;其各自的优点就是对方的缺点&#xff0c…

【C++学习】list的使用及模拟实现

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; list的使用及模拟实现&#x1f63c;构造函数&#x1f435;模拟实现&#x1f63c;迭代器&#x1f435;…

[软件工程导论(第六版)]第6章 详细设计(复习笔记)

文章目录6.1 结构程序设计6.2 人机界面设计6.3 过程设计的工具6.3.1 程序流程图&#xff08;程序框图&#xff09;6.3.2 盒图&#xff08;N-S图&#xff09;6.3.3 PAD图&#xff08;问题分析图&#xff09;6.3.4 判定表6.3.5 判断树6.3.6 过程设计语言6.4 面向数据结构的设计方…

Spring Cloud Nacos实战(二)- 服务提供者注册

Nacos服务提供者注册 Nacos代替Eureka ​ Nacos可以直接提供注册中心&#xff08;Eureka&#xff09;配置中心&#xff08;Config&#xff09;&#xff0c;所以它的好处显而易见&#xff0c;我们在上节课成功安装和启动了Nacos以后就可以发现Nacos本身就是一个小平台&#xf…

解决IDEA报错:无效的目标发行版: 17

解决IDEA报错&#xff1a;无效的目标发行版: 17 目录解决IDEA报错&#xff1a;无效的目标发行版: 17报错由来解决报错【1】检查setting设置&#xff0c;查看编译器编译模块的编译版本是否是你需要的【2】尝试去修改当前项目的启动设置&#xff0c;设置JRE为你需要的版本。【3】…

odoo如何完成员工借款、报销、归还余款的会计操作?【业财一体】

ODOO中如何实现财务场景&#xff1a;一个员工从公司借款1000元&#xff0c;然后用这个借款&#xff0c;支付了电费500元&#xff0c;然后又归还剩余500元呢&#xff1f; 一、基本流程如下&#xff1a; 二、基础科目的设置 1.1 预付账款科目&#xff1a;设置在 产品【Employee…

DQL 多表查询

1、多表关系 一对多&#xff08;多对一&#xff09; 案例: 部门 与 员工的关系 关系: 一个部门对应多个员工&#xff0c;一个员工对应一个部门 实现: 在从表的一方建立外键&#xff0c;指向主表一方的主键 多对多 案例: 学生 与 课程的关系 关系: 一个学生可以选修多门课程&am…

Revisiting Distributed Synchronous SGD 带有Back-up机制的分布式同步SGD方法 论文精读

论文链接&#xff1a;Revisiting Distributed Synchronous SGD ABS 本文介绍了用于分布式机器学习的同步和异步SGDSGDSGD&#xff0c;同时指出各自的缺点&#xff1a;stragglersstragglersstragglers和stalenessstalenessstaleness。 同时为了解决同步SGDSGDSGD存在straggle…

【go语言grpc之client端源码分析二】

go语言grpc之server端源码分析二DialContextparseTargetAndFindResolvergetResolvernewCCResolverWrapperccResolverWrapper.UpdateStatecc.maybeApplyDefaultServiceConfigccBalancerWrapper.updateClientConnState上一篇文章分析了ClientConn的主要结构体成员&#xff0c;然后…

扫雷小游戏 - C语言

目录 一、扫雷游戏 1.游戏一共创建使用了三个文件 2.test.c 文件代码&#xff08;游戏逻辑&#xff09; 3.game.h - 必要的函数声明代码 4.game.c 游戏所有函数实现代码 一、扫雷游戏 1.游戏一共创建使用了三个文件 test.c - 测试游戏逻辑 game.h - 游戏代码包含的函数…

低频量化之指数 PE-PB-偏离 数据

目录历史文章股票明日涨停预测指数PEPB分位指数PE分位指数PB分位行业指数PEPB分位行业指数PE分位行业指数PB分位指数60日线偏离数据指数MA60偏离统计上证指数 MA60偏离度深证成指 MA60偏离度创业板指 MA60偏离度中小100 MA60偏离度上证50 MA60偏离度沪深300 MA60偏离度中证500 …

代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集

文章目录01背包基础 &#xff08;二维数组&#xff09;思路递推公式初始化遍历顺序一维dp数组&#xff08;滚动数组&#xff09;一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 &#xff08;二维数组&#xff09; 思路 根据动态规划五部进行分析&a…

Vulnhub 渗透练习(八)—— THE ETHER: EVILSCIENCE

环境搭建 环境下载 靶机和攻击机网络适配都选 NAT 即可。 信息收集 主机扫描 两个端口&#xff0c;22 和 80&#xff0c;且 apache httpd 2.4.0~2.4.29 存在换行解析漏洞。 Apache HTTPD是一款HTTP服务器&#xff0c;它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中…

跨域问题的三种解决办法

我们平时对于前后端联调的项目&#xff0c;以下的错误是经常常见的&#xff0c;我们查看浏览器报错&#xff1a; Access to XMLHttpRequest at http://localhost:63110/system/dictionary/all fromorigin http://localhost:8601 has been blocked by CORS policy: No Access…

自动化测试5年经验,分享一些心得

自动化测试介绍 自动化测试(Automated Testing)&#xff0c;是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架&#xff0c;编写自动化测试用例&#xff0c;来模拟手工测试过程。比如说&#xff0c;在项目迭代过程中&#xff0c;持…