iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Software Development

閱讀 Linux Kernel 文件系列 第 9

# Day 9 Cache and TLB Flushing Under Linux (一)

如同 Day1 簡介的,這份文件是之前工作中有碰過 cache & TLB 相關的項目,但是沒來得及好好閱讀的文件,趁這個機會好好的研究研究。

文件

Original: Documentation/core-api/cachetlb.rst

========================
Linux 下的快取和 TLB 更新
========================

:作者: David S. Miller <davem@redhat.com>

本文描述了由 Linux 虛擬記憶體子系統使用的快取/TLB 更新的應用程式介面(API)。
它列舉了每個介面,描述了它的預期目的,以及使用介面後預期的作用。

下面描述的作用是針對單核處理器的實做,以及在該單核處理器上會發生的狀況。
若為 SMP (多核處理器),則只需簡單的使某個特定介面在系統所有處理器上發生作用。
不要被這句話嚇到,認為 SMP 的快取/TLB 更新一定是很沒有效率的,
事實上,這是一個可以進行很多優化的區域。
例如,如果可以證明一個使用者的定址空間從未在某個 cpu 上執行過(見mm_cpumask()),
那麽就不需要在該 cpu 上對這個定址空間進行更新。

首先是 TLB 更新介面,因為它們是最簡單的。
在 Linux 下,TLB 被抽象為 cpu 軟體分頁表所取得的"虛擬 -> 實體記憶體轉換"的快取;
這意味著,如果軟體分頁表發生變化,這個 "TLB" 中就有可能存在過期的轉換記錄。
因此,當軟體分頁表發生變化時,核心會在分頁表發生 *變化後* 呼叫以下其中一種更新介面:

1) ``void flush_tlb_all(void)``

    最全面的更新。在這個介面執行過後,cpu 可以看到任何以前的分頁表變化。

    這通常是在核心分頁表被改變時使用的,因為這種轉換在本質上是“全域性”的。

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

    這個介面更新了整個使用者定址空間。
    在執行後,這個介面必須確保 cpu 能夠看見對定址空間 ‘mm’ 的任何分頁表修改。
    也就是說,在執行後,TLB 中不會有 ‘mm’ 的分頁表項。

    這個介面被用來處理整個地址空間的分頁表操作,
    比如在 fork 和 exec 過程中發生的事情。

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

    這裡我們要從 TLB 中更新一個特定範圍的使用者虛擬記憶體位址轉換。
    在執行後,這個介面必須確保 cpu 可以看見對 ‘start’ 到 ‘end-1’ 範圍內的地址空間
    ‘vma->vm_mm’ 的任何分頁表修改。也就是說,在運行後,TLB 中
    不會有 ‘mm’ 的分頁表項存在 ‘start’ 到 ‘end-1’ 範圍內的虛擬地址。

    “vma” 是該區域的備份。這個介面主要是用於 munmap() 類型的操作。

    提供這個介面是希望能夠找到一個有效率方法來從 TLB 中刪除多個頁面大小的轉換,
    而不是讓核心為每個可能被修改的分頁表項使用 flush_tlb_page(見下文)。

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

    這次我們需要從 TLB 中刪除 PAGE_SIZE 大小的轉換。
    ‘vma’ 是 Linux 用來紀錄程序 mmap 區域的資料結構,
    定址空間可以通過 vma->vm_mm 取得。另外,
    可以通過測試(vma->vm_flags & VM_EXEC)來查看這個區域是否是
    可執行的(因此在 split-tlb 類型中,這個區域可能在 “指令 TLB” 中)。

    在運行後,這個介面必須確保 cpu 可以看見任何先前對使用者虛擬位址 “addr” 
    的定址空間 “vma->vm_mm” 所進行的分頁表修改。
    也就是說,在運行後,TLB 中不會有虛擬地址 ‘addr’ 的 ‘vma->vm_mm’ 的分頁表項。

    這主要是在錯誤處理時使用。

5) ``void update_mmu_cache(struct vm_area_struct *vma,
   unsigned long address, pte_t *ptep)``

    在每個分頁錯誤結束時,這個介面被執行以告訴架構相關的程式碼,
    在軟體分頁表中,在定址空間 “vma->vm_mm” 的虛擬位址 “address” 處,
    現在存在一個位址轉換。

    可以使用這個資訊用任何方式來進行移植。例如,
    可以使用這個介面來為軟體管理的 TLB 預先填充 TLB 轉換資訊。
    目前 sparc64 架構就是這麽實作的。

我的理解

這份文件描述的是 Linux 底下,快取和 TLB 的更新操作介面,今天閱讀的部分是 TLB 更新的介面,就字面上意思來做理解的話,其實並不算困難,簡單的統整一下今天閱讀到的內容:

  • struct mm_struct 是用來描述一個 struct task_struct 所有的虛擬記憶體定址空間
  • struct vm_area_struct 則是用來描述 struct mm_struct 中 mmap 的資訊
  1. flush_tlb_all:更新所有的 TLB 內容,通常用於 kernel 做分頁表更新後
  2. flush_tlb_mm:更新 TLB 中,使用者程序的所有虛擬記憶體位址的轉換,通常用於 fork or exec
  3. flush_tlb_range:更新 TLB 中,使用者程序的虛擬記憶體位址轉換的某幾個分頁表項,通常用於 munmap,相較於一個一個 page 的刪除,這是一個較有效率的做法
  4. flush_tlb_page:更新 TLB 中,使用者程序的一個 page 大小的虛擬記憶體位址轉位,通常用於 fault handler
  5. update_mmu_cache:在 page fault 處理結束後,用來告知 kernel,哪部分 addr 的轉換已更新

難的地方在於,實際找到一個 running example,可以動態的觀察和追蹤整個過程,並同時了解 struct mm_struct 和 struct vm_area_struct 的詳細資訊。

後記

文件寫的輕巧簡潔,但是真的要每個文件上描述的事情都完全了解,要花的功夫可不少;今天光看了 TLB 部分,就產生了一大堆問題,例如:

  • TLB 是個實際硬體嗎?還是軟體模擬?若是硬體的話,會在哪裡呢?
  • struct mm 的實際使用情形是? struct vm_area_struct 呢?
  • MMU 和 TLB 的關係是什麼? 在實際程式碼中,看的到相關的訊息嗎?
  • 上述提到的所有 API 都是每個架構自己實作自己的版本嗎?... 等等

今天的內容就先到這,接下來的幾篇會繼續閱讀這篇文件,整理並記錄所有的疑問,然後試著找到答案!
感謝各位,我們明天見!


上一篇
# Day 8 Why the “volatile” type class should not be used
下一篇
# Day 10 Cache and TLB Flushing Under Linux (二)
系列文
閱讀 Linux Kernel 文件30

尚未有邦友留言

立即登入留言