那麼接續昨天的閱讀,我們繼續看下去~
接下来,有快取的更新介面。一般來說,當 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 版本,但有些相關的名詞是不太熟悉的,先簡單的做個整理:
這部分的文件沒有解答昨天有的疑問,反而讓我有更多不了解的部分XDDD
然後也延伸出了很多值得做實驗觀察的部分:
kernel 的記憶體管理子系統真的是很複雜呢!有好多東西可以研究!
那麼今天就先到這,整份文件分為四個部分
https://yushuanhsieh.github.io/post/2021-02-18-l1-cache-arm/
用 VIVT/VIPT/PIPT 當關鍵字就有許多可以看的了~