CSAPP: 第九章笔记

虚拟地址是由虚拟页号(vpn)和虚拟页偏移(vpo)构成

vp指虚拟页,磁盘存放的是虚拟页的数据

使用malloc申请内存,发生在用户态,只是做标记;改变页表,发生在缺页中断,操作系统会分配真实的物理内存地址,并改变pte,有效位置1。当内存严重不足或者内核主动回收内存或者页面长时间未使用时,即当物理内存压力比较大,需要为其他页面腾出空间时,操作系统才会将内存的数据放入硬盘交换区,并改变pte,将pte地址改成磁盘扇区编号,并将有效位改成0,只有这样,cpu才会把pte有效位后面的当成磁盘扇区编号

如果VP4已经被修改了,那么内核就会将它复制回磁盘。尤论哪种情况,内核都会修改VP4的页表条目,反映出VP4不再缓存在主存中这一事实。

数据查询流程图:

内核态

命中

不命中

从虚拟地址中提取vpn将vpn作为页表索引查询页表

有效

送入主存

无效

地址字段为null

地址字段不为null

在此例子中,牺牲页是PP3,当前缓存是VP4

修改过

没有修改过

重新执行导致缺页的指令

虚拟地址(VA)

mmu

TLB(该部件在mmu内部)

是否命中

data

查询页表

页表

判断对应pte有效位是否有效

取出PTE中物理页号(PPN),与虚拟地址的VPO拼成物理地址

主存

触发缺页异常处理程序
缺页异常处理程序还会检查访问权限,如果违法触发保护异常

判断地址字段是否为空

段错误
表示未分配
segmentation fault

选择牺牲页

检查牺牲页是否为脏页,即是否被修改过

说明此时PP3的数据和磁盘中VP4数据不一致,需要先将PP3的数据写回磁盘VP4的位置

直接丢弃PP3的数据

更新页表VP4对应的PTE,将对应的有效位置0,确保地址字段指向磁盘位置(如果未分配则设为磁盘地址,如果已有则保持)

调入新页面,读取VP3的数据,存入刚刚释放的物理页PP3中

更新页表,将VP3对应的PTE有效位置1,地址字段改为PP3的物理页号

返回用户态,返回导致CPU触发缺页中断的地方

练习题9.3

  1. VPO 指虚拟页偏移,PPO指物理页偏移
    VPO=PPO=log2PVPO = PPO = log_2PVPO=PPO=log2P
  2. VPN 指虚拟页号,N指虚拟地址总位数
    VPN=N−VPOVPN = N - VPOVPN=NVPO
  3. PPN 指物理页号,M指物理地址总位数
    PPN=M−PPOPPN = M -PPOPPN=MPPO

内存映射是将磁盘或固态上的文件内容映射到进程的虚拟地址空间中,使得进程可以通过读写内存来直接访问文件数据,而不需要显示read/write

练习题9.5

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>

void mmapcopy(int fd, int size) {
    char *bufp;

    // mmap: 将文件映射到内存
    // NULL      → 让内核选择映射地址
    // size      → 映射大小(整个文件)
    // PROT_READ → 只读
    // MAP_PRIVATE → 私有映射(写时复制)
    // fd        → 文件描述符
    // 0         → 从文件开头开始映射
    bufp = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (bufp == MAP_FAILED) {
        perror("mmap failed");
        exit(1);
    }

    // 把映射到内存的文件内容写到标准输出(stdout 的文件描述符是 1)
    write(1, bufp, size);

    // 解除映射
    munmap(bufp, size);
}

int main(int argc, char *argv[]) {
    struct stat statbuf;
    int fd;

    if (argc != 2) {
        printf("用法: %s <filename>\n", argv[0]);
        exit(0);
    }

    // 1. 打开文件,获取 fd
    fd = open(argv[1], O_RDONLY, 0);
    if (fd < 0) {
        perror("open failed");
        exit(1);
    }

    // 2. 用 fstat 获取文件大小
    if (fstat(fd, &statbuf) < 0) {
        perror("fstat failed");
        close(fd);
        exit(1);
    }

    // 3. mmap 映射并输出
    mmapcopy(fd, statbuf.st_size);

    // 4. 关闭文件
    close(fd);
    return 0;
}

图 9-43 理解

00      00 00 00 21
01      00 00 00 00
02      00 00 00 00
03      00 00 00 00
04      00 00 00 00
05      00 00 00 00
06      00 00 00 00
07      00 00 00 21
08      00 00 00 11
09 bp-> 00 00 00 00
0A      00 00 00 00
0B      00 00 00 11

#define PREV_BLKP(bp) ((char *) (bp) - GET_SIZE(((char *) (bp) - DSIZE)))
bp-DSIZE -> 07
GET_SIZE 获取上一个块的大小,这里是32

参考资料

教材:Randal E. Bryant, David R. O’Hallaron. Computer Systems: A Programmer’s Perspective (Third Edition). 第 9 章《虚拟内存》(Virtual Memory).

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐