从 ThreadOS 到 ProcessOS

从 ThreadOS 到 ProcessOS

2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

TheadOS: Trap & 系统调用

syscall: “跳转并获得无限的权力”

center

syscall = “jal”:  // sysret: 逆操作
    mov %rip, %rcx
    mov %rflags, %r11
    set SS = kernel, SS = kernel, CPL = 0
    jmp IA32_LSTAR  // System Target Address Register
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

进程 = 戴上 VR 眼镜的线程

一个函数 ff

  • 使所有对内存地址 xx 的访问都转换为 f(x)f(x)

center

2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

实现 VR 眼镜

1. 规定好 ff 的描述方法

struct page_table_entry {
    uint32_t vpn, ppn, flags; // valid & protection
} page_table[16];

2. 提供一条 “戴上 VR 眼镜” 的指令

void arm_with_vr(page_table *pt) {
    asm volatile(
        "set_ptbr %0" : : "r"(pt) : "memory"
    );
}
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

普通实现:x86, ARM, RISC-V, ...

ff (内存部分): Radix Tree

  • 一个 1024 叉树 (64-bit: 512 叉)
    • i386: 2 层,正好 10 + 10 + 12
    • x86_64: PML4/PML5
      • 也有三级页表 (sv39,索引 512 GB)

ff (处理器部分): CR3 (PDBR)

struct page *cr3;  // 指向数据结构根的指针
  • Translation-Lookaside Buffer (TLB): 缓存
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

文艺实现:MIPS

ff (内存部分): 随操作系统喜欢

  • ???

ff (处理器部分): Software-managed TLB

struct tlb_entry {
    uint32_t vpn, ppn, flags;
} tlb[16];
  • TLB Miss 以后产生异常
    • 操作系统使用 tlbwi/tlbwr (indexed/random) 重填
    • 甚至可以为不同的进程使用不同的数据结构!
  • 也许 CPU 的各个部分都会变得更 programmable
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

疯狂实现:Inverted Page Table

ff (内存部分): 所有进程共享的哈希表

  • key=(VPN, pid), value=PPN
    • 假设所有进程足够 ASLR
    • 适合地址空间比物理内存大得多的场合

ff (处理器部分): 所有进程共享的 IPTR

  • 当然,TLB 还是需要的
  • TLB miss 的时间有点 “不可控”
    • 哈希表:最好 O(1)O(1),最坏 O(n)O(n) 😂
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

再看 libbloat.so

void bloat() {
    // 100M of nops
    asm volatile(
        ".fill 104857600, 1, 0x90"
    );
}
  • 1,000 个进程也不会榨干我的物理内存
  • 这是毫无疑问的
    • 操作系统可以掌控 ff
    • 不同进程的不同 vpn 可以指向同一个 ppn
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

再告诉大家两个秘密

就算是 100G 的 libbloat.so

就算是 100G 的随机代码

  • 还是不会!
  • 但是会使我的系统卡顿 😂
    • Demand paging
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

Demand Paging

进程能做的

  • 执行一条 load/store 访问一个地址

操作系统看到的

  • 数据结构维护的区间列表 (pmap)
  • 有些部分分配了,有些部分没分配
    • 些页面要保留在内存,操作系统说了算
  • 如果发生 Page Fault
    • 合法,未分配:改变 ff (数据结构),映射一页
    • 非法:SIGSEGV
2024 南京大学《操作系统:设计与实现》
从 ThreadOS 到 ProcessOS

Copy-on-write fork()

函数 ff 的妙用

  • fork() 后直接把父子进程地址空间标记成只读
    • 在最顶层页表标记即可
  • 任何合法的 store Page Fault 时就会分成两种情况
    • 未分配:直接分配
    • 已分配 (read-only):复制一份
      • share_count--
      • share_count == 1 最后一份只读副本也变成可写
2024 南京大学《操作系统:设计与实现》