# Day 10 Cache and TLB Flushing Under Linux (二)

那麽接续昨天的阅读,我们继续看下去~

文件

接下来,有快取的更新介面。一般来说,当 Linux 要将现有的虚拟->实体记忆体转换
改变为新的值时,其顺序将是以下形式之一::

    1) flush_cache_mm(mm);
        change_all_page_tables_of(mm);
        flush_tlb_mm(mm);

    2) flush_cache_range(vma, start, end);
        change_range_of_page_tables(mm, start, end);
        flush_tlb_range(vma, start, end);

    3) flush_cache_page(vma, addr, pfn);
        set_pte(pte_pointer, new_pte_val);
        flush_tlb_page(vma, addr);

快取级别的更新将永远是第一位,因为这让我们能正确处理那些对快取要求严苛,
且当该位址正被从快取中更新时,需要其虚拟->实体转换存在的系统。
HyperSparc cpu 就是一个具有这种属性的 cpu。

下面的快取刷新介面只需要在特定的 cpu 需要的范围内处理快取更新。
大多数情况下,使用虚拟索引快取的 cpu 上,这些介面必须被实作,
当虚拟->实体转换被改变或移除时,这个虚拟索引的快取必须被更新。
因此,例如,IA32 处理器使用物理索引、物理标记的快取就没有必要实做这些介面,
因为这些快取是完全同步的,并且不依赖转换的讯息。

下面逐一列出这些介面:

1) ``void flush_cache_mm(struct mm_struct *mm)``

    这个介面将整个使用者定址空间从快取中清除。
    也就是说,在执行後,将没有与 ‘mm’ 相关的快取区块。

    这个介面被用来处理整个定址空间的分页表操作,
    比如在 exit 和 exec 过程中发生的事情。

2) ``void flush_cache_dup_mm(struct mm_struct *mm)``

    这个介面将整个使用者定址空间从快取中清除。
    也就是说,在执行後,将没有与 ‘mm’ 相关的快取区块。
    
    这个介面被用来处理整个定址空间的分页表操作,
    比如在 fork 过程中发生的事情。

    这个选项与 flush_cache_mm 分开,以对 VIPT 快取进行优化。

3) ``void flush_cache_range(struct vm_area_struct *vma,
   unsigned long start, unsigned long end)``

    在这里,我们要从快取中更新一个特定范围的(使用者)虚拟位址。
    执行後,在 “start” 到 “end-1” 范围内虚拟位址的 “vma->vm_mm”,
    快取中将没有其分页表项。

    “vma” 是该区域的备份。这个介面主要是用於 munmap() 类型的操作。

    提供这个介面是希望能够找到一个有效率方法来从块取中删除多个页面大小的转换,
    而不是让核心为每个可能被修改的分页表项使用 flush_cache_page(见下文)。

4) ``void flush_cache_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn)``

    这一次我们需要从快取中删除一个 PAGE_SIZE 大小的区间。
    ‘vma’ 是 Linux 用来纪录程序 mmap 区域的资料结构,
    定址空间可以通过 vma->vm_mm 获得。另外,
    我们可以通过测试(vma->vm_flags & VM_EXEC),
    来查看这个区间是否是可执行的
    (这块区间在 “Harvard” 类型的快取中可能是在“指令快取”内)。

    “pfn” 表示 “addr” 所对应的实体分页页框
    (通过PAGE_SHIFT左移这个值来获得实体位址)。
    而这个映射应该从快取中被删除。

    在执行之後,被转换成 ‘pfn’ 的虚拟位址 ‘addr’ 
    的 ‘vma->vm_mm’,在快取中不会有任何分页表项。

    这主要是在错误处理过程中使用。

5) ``void flush_cache_kmaps(void)``

    只有在平台使用 highmem 的情况下才需要实做这个介面。
    它将在所有的 kmaps 失效之前被呼叫到。

    执行後,核心虚拟位址范围 PKMAP_ADDR(0) 到 PKMAP_ADDR(LAST_PKMAP)
    的快取中将没有分页表项。

    这个介面应该在 asm/highmem.h 中实做。

6) ``void flush_cache_vmap(unsigned long start, unsigned long end)``
   ``void flush_cache_vunmap(unsigned long start, unsigned long end)``

    在这两个介面中,我们从快取中更新一个特定范围的(核心)虚拟位址。
    执行後,核心定址空间的快取中不会有,在 “start” 到 “end-1” 范围内虚拟位址的分页表项。

    这两个介面中的前者是在 vmap_range() 装载了分页表项之後呼叫的。
    第二个则是在 vunmap_range() 删除分页表项之前呼叫的。

我的理解

读完这个部分,大致上和昨天读到的内容有些相似,换成了 cache 版本,但有些相关的名词是不太熟悉的,先简单的做个整理:

  1. flush_cache_mm:更新 cache 中某个使用者程序的定址空间,通常用在 exit 或是 exec
  2. flush_cache_dup_mm:和上述相同,不同之处为,在 VIPT 快取上使用可以达到某种程度的优化
  3. flush_cache_range:一次更新 cache 中 struct mm_struct 的多个 PAGE_SIZE 范围
  4. flush_cache_page:一次更新 cache 中一个 PAGE_SIZE 范围
    • 这边 harvard 架构应该只是想描述 icache dcache 分开的状况
  5. flush_cache_kmaps:支援 highmem config
  6. flush_cache_vmap、flush_cache_vunmap:类似於 flush_cache_range,不过是针对核心定址空间做更新

後记

这部分的文件没有解答昨天有的疑问,反而让我有更多不了解的部分XDDD

  • PIPT、VIPT、VIVT 详细的运作方式以及优缺点
  • 为什麽 PIPT 会不需要做 cache flush
  • highmem、kmap 相关 (( highmem 是让 32 位元的 kernel 可以定址到超过 32 位元所能定址的范围
  • vmap、vunmap、vmap_range、vunmap_range 相关

然後也延伸出了很多值得做实验观察的部分:

  • 从上电到开机完成,PA 是怎麽被管理的?
  • page table 是怎样被建立的?

kernel 的记忆体管理子系统真的是很复杂呢!有好多东西可以研究!
那麽今天就先到这,整份文件分为四个部分

  1. 更新 TLB 的相关介面
  2. 更新 cache 的相关介面
  3. 处理 Dcache aliasing (蛤?)
  4. 处理 I/O 的相关介面
    接下来对於这分文件系列的规划是,明天预计阅读第三部分的文件,後天第四,再来是 Q&A,也许可以做 2 篇,那麽就感谢大家观看,我们明天见!

<<:  Day 4 Ruby 变数与资料型别 Variable and Data Type

>>:  Day 19 - Unreal Webcam Fun [更新]

JavaScript Day15 - event(2)

event 查目前网页的 event,开启 Chrome 的开发者工具,点选 Elements,之後...

Day 19 Compose UI Animation I (Rotation)

今年的疫情蛮严重的,希望大家都过得安好,希望疫情快点过去,能回到一些线下技术聚会的时光~ 今天目标:...

第28天-CSS-影像-(3-2)

重复影像 background-repeat 这个属性可以重复图像在背景 如果是使用小图做素材 可以...

[Day 03]取得Nonce与HashID以产出Sign - [C#]丰收款API必备前置作业(二)

首先来个永丰官方的文件图片作开场吧! (图一:由商户->永丰正向发动的所需参数) 而我们今天要...

[Day1] 资讯安全是什麽?

第一天,想笔记下我对资讯安全的理解。 资讯安全怎麽来的? 我印象里的历史战争剧中,国家或两个阵营打仗...