簡介
- per-CPU變量是內核的一個重要機制,正如名稱所示,per-CPU變量爲每個cpu單獨提供內存空間,每個cpu只訪問修改各自的空間
- 一個per-CPU變量所需要的內存大小爲:變量類型大小乘以cpu數量,即sizeof(type) x (number of cpus)
- 在NUMA系統中,計算所佔內存大小的公式與上面類似,但是實際上cpu數量的計算更復雜
- 事實上,不管哪一種同步API都會帶來或多或少的性能開銷,但是一個per-CPU數據包含每個cpu自己的唯一私有數據,一個cpu不應該訪問其他cpu對應的數據,所以按照這樣邏輯不再需要使用同步API。注意:“只有這個cpu能訪問這個數據“就是一種編程約定,我們需要確保當前cpu只會訪問它自己的唯一數據。
- 很多內核子系統使用了這個機制:
- 內存管理的高速緩存機制
- Page分配器
- SLUB/SLAB分配器
- 經常需要更新的性能統計計數器,需要高效同步機制的例子
- vm統計
- 網絡統計
- 基礎設施RCU
- 性能剖析profiling、Ftrace
- VFS虛擬文件系統
- 內存管理的高速緩存機制
下圖是一個簡單的per-CPU計數器例子
特點
- 從2.6.19內核開始,爲了方便創建和操作per-CPU數據,從而引入新的接口,函數定義單獨放在allocpercpu.c文件,對於大型SMP計算機來說這些操作per-CPU數據接口簡單且方便高效
- 2009年合併了動態percpu分配的補丁,引入了基於chunk實現的各種函數
- 將以前percpu變量所佔內存都分別從每個cpu對應的內存區域裏分配,也就變成chunk模式了。
- 可以通過相同的接口直接訪問靜態和動態percpu區域
- 參考:
Unit
- 每個cpu數據將放入每個cpu對應的unit內存空間
- unit不僅可能包含三種不同類型的內存區域,也有可能只包含一種內存區域
- Static內存區域
- 使用
DEFINE_PER_CPU()
宏聲明per-CPU變量,其被編譯進kernel映像中,在系統啓動時爲每個cpu定義這個變量 - 默認大小0x3ec0
- 使用
- Reserved內存區域
- 使用
DEDINE_PER_CPU()
宏聲明per-CPU變量,其被編譯進模塊文件中,在加載模塊時爲每個cpu定義這個變量 - 默認大小0x2000(8KiB)
- 使用
- Dynamic 內存區域
- 藉助
alloc_percpu
函數動態地分配每個cpu變量所佔的內存空間 - 默認大小0x5000(20KiB)
- 在64位系統上,默認大小28KiB
- 藉助
- Static內存區域
- unit大小等於
- pcpu_unit_pages
- 組成一個Unit的頁幀數
- 通常大小爲 16
- pcpu_unit_szie
- 組成一個Unit的字節大小(單位Byte)
- 通常大小爲 0x10000(64KiB = 16 * 4KiB (PAGE_SIZE))
- pcpu_unit_pages
CPU -> Unit 映射
下圖是一個在NUMA系統中cpu與unit映射的例子,(這個NUMA系統最大支持32CPU,實際有12CPU可用)