天天看點

mime類型是什麼 node_Linux中kmem_cache_node和 kmem_cache這兩個對象詳解

開局一張圖

mime類型是什麼 node_Linux中kmem_cache_node和 kmem_cache這兩個對象詳解

slab_allocator記憶體配置設定關系圖

1. struct kmem_cache這個結構體類型的作用

該結構體的作用是用于管理大小為size(struct kmem_cache.size)的對象在各個node(NUMA架構)中的哪些slab page中配置設定。

其中的struct kmem_cache.node[MAX_NUMNODES].partial用于維護每個node中有哪些slab page被配置設定用于對該size大小對象的allocate request。

當我們發起一個object allocate request後,其實不是先從

struct kmem_cache.node[req_node].partial連結清單中查找合适的slab page并從中配置設定的,而是會先從struct kmem_cache.cpu_slab(struct kmem_cache_cpu __percpu類型)中查找slab page,如果沒有找到,會将struct kmem_cache.node[req_node].partial中的slab page移到cpu_slab中,然後再從cpu_slab中配置設定,這樣cpu_slab中就維護了一個special slab page list,這樣可以提高配置設定的效率,該結構體struct kmem_cache_cpu是從cpu_area中單獨配置設定的。

具體做法如下:

(1) struct kmem_cache_cpu.page 維護一個從kmem_cache.node[req_node].partial中移來的一個slab page。

(2) struct kmem_cache_cpu.partial這個雙向連結清單維護從

kmem_cache.node[req_node].partial中移來的多個slab pages,構成一個fallback list。

(3) 對struct kmem_cache.size大小的對象的配置設定是先從struct kmem_cache.page.freelist中配置設定。

(4) 如果struct kmem_cache.page.freelist==null或struct kmem_cache.page == null,那麼就從struct kmem_cache.partial中移除一個到struct kmem_cache.page中後再配置設定。

(5) 如果struct kmem_cache.partial中也沒有合适的slab page的話那麼就要從

struct kmem_cache.node[req_node].partial中移除若幹個slab pages到

struct kmem_cache.page &.partial中,然後繼續配置設定。

(6) 如果struct kmem_cache.node[req_node].partial中也沒有合适的slab page呢,那就要從對應的buddy system: node_data[req_node].node_zones[zonetype].free_area[page-order].free_list[migreate_type]中配置設定合适的slab page。

(7) 如果req_node對應的buddy system中都沒有合适的slap page呢?那就調用get_any_partials從其它node中配置設定合适的slab page,從這一步可以看出struct kmem_cache_cpu.partial這個連結清單中維護的slab page是有可能來自不同的node。

2.

kmem_cache_node對象的作用(struct kmem_cache類型)

該對象的作用是為了配置設定struct kmem_cache_node類型對象。

struct kmem_cache_node類型對象是用來管理其對應node中的一些slab pages,而這些slab pages适用于配置設定哪些對象是由包含struct kmem_cache_node對象的struct kmem_cache.size定義的。

kmem_cache_node.node[]數組是

struct kmem_cache_node*類型數組

,其每個元素指向一個struct kmem_cache_node類型對象,每個struct kmem_cache_node類型對象記錄了大小為struct kmem_cache.size的對象應該在哪些slab page中配置設定;而這個kmem_cache_node對象(struct kmem_cache類型)就是用來描述系統中所有struct kmem_cache_node類型對象是如何配置設定的,具體就是通過kmem_cache_node.node[]指向的struct kmem_cache_node對象在指定的node中配置設定struct kmem_cache_node對象;是不是有點繞,這就是所謂的

chicken-egg dilemma

問題。

Linux early bootstrap階段是如何解決這個問題的呢?

2.1 系統會調用new_slab()函數從會從每個node自己的buddy system中為其配置設定一個page(page-order0=4K)用于配置設定各自node中的struct kmem_cache_node對象。

2.2 每個node新配置設定的page.freelist維護一個單向free_object_addr位址連結清單,該連結清單記錄了在該slab page中哪些位置是空閑的可以用來配置設定struct kmem_cache_node對象,預設是指向該struct page所代表的PFN的基位址,每個free_object_addr的首部8/4bytes用于存儲下一個free_object_addr的位址,這樣就構成一個list了。

2.3 kmem_cache_node.node[n]數組指向的每個struct kmem_cache_node對象占用每個node在2.1步驟中配置設定的page.freelist指向的第一個free_object_addr,然後将page.freelist指向下一個free_object_addr,然後設定kmem_cache_node.node[n].partial = &page.slab_list,将該page加入到struct kmem_cache_node.partial(listhead類型),這樣該page就被這個struct kmem_cache_node類型對象管理了,用于配置設定struct kmem_cache_node類型對象,這樣就解決了chicken-egg問題了。

從partial是個listhead雙向連結清單類型可以看出,一旦該struct kmem_cache_node對象管理的空間不足了,就繼續從buddy system中配置設定新的page加入partial list即可。

至于為什麼用partial呢,這個大家應該有所了解了吧,就是因為這個chicken-egg問題導緻一個完整page有部分(第一個 free_object_addr)被占用了,是以隻有部分空間可以繼續用于該對象的配置設定了。

還有當req_node!=page_to_node(page)時會導緻調用deactivate_slab(),該函數會将struct kmem_cache.cpu_slab.page釋放到對應的struct kmem_cache.node[].partial中,該page很有可能部分被使用過了,當然新配置設定的slab page也會放在該partial list,而且是被插入隊頭,而struct kmem_cache.node[].full list裡維護了所有已經full allocated slab page,這個清單主要是為了debug。

3. kmem_cache對象的作用(struct kmem_cache類型)

該kmem_cache對象目的是為了配置設定struct kmem_cache對象。

kmem_cache.node[node_nr]數組中指向的每個struct kmem_cache_node對象管理了其對應node中的一些slab page,用于配置設定kmem_cache.size(sizeof (struct kmem_cache))大小記憶體給struct kmem_cache對象,而kmem_cache.node[node_nr]中指向的每個類型為

struct kmem_cache_node的對象,就是從kmem_cache_node.node[node_nr].partial中配置設定的,當然要通過kmem_cache_node.cpu_slab.page.freelist間接配置設定;這些新配置設定的struct kmem_cache_node對象記錄了每個node中能用于配置設定struct kmem_cache類型對象的slab page。

4. 下面詳細介紹如何配置設定一個新的struct kmem_cache_node類型對象

例如3中介紹的struct kmem_cache kmem_cache對象就是用于配置設定對象大小為sizeof(struct kmem_cache)的對象,又是用于配置設定自己的,是以為其配置設定slab page分為如下幾個步驟:

4.1 首先要為kmem_cache.node[node_nr]數組配置設定node_nr個struct kmem_cache_node對象,該對象用于管理對應node中哪些slab page可用于配置設定struct kmem_cache類型對象,這時根據nodeId定位到2中介紹的kmem_cache_node.node[nodeId]指向的

struct kmem_cache_node對象,這樣就可以通過該struct kmem_cache_node.partial指向的slab_list,知道哪些slab page可以用于配置設定struct kmem_cache_node對象了,然後得到的對象位址直接指派給kmem_cache.node[nodeId]就完成了一個新的struct kmem_cache_node類型對象的配置設定了,但不是直接從struct kmem_cache_node.partial中直接配置設定的。

4.2 對象都是從struct kmem_cache.cpu_slab((struct kmem_cache_cpu __percpu類型)中的配置設定的

具體來說是直接從struct kmem_cache.cpu_slab.page.freelist(指向一個slab page,注意不是list)中配置設定對象的,這個page是從struct kmem_cache.node[nodeId].partial中挪出來的,至于挪用多少要根據struct kmem_cache.min_partial來決定,如果min_partial > 1那麼,挪出來的多餘的(min_partial-1)個slab page就會插入到struct kmem_cache.cpu_slab.partial中,這樣當struct kmem_cache.cpu_slab.page為空(用完了就會被remove)的時候就會從struct kmem_cache.cpu_slab.partial中補充一個到struct kmem_cache.cpu_slab.page上,依次循環下去。

4.3 struct kmem_cache_cpu __percpu類型

__percpu這個修飾符表明它是要從cpu_area中配置設定記憶體的,既從pcpu_reserved or pcpu_dynamic region中配置設定,要通過pcpu_allocator配置設定(這部分非常複雜以後有時間詳解)

kmem_cache_cpu.page(& .partial)維護了一個特殊的slab page list(來自于對應的struct kmem_cache_node),這樣更利于對象的配置設定。

4.4 struct kmem_cache類型對象的真正配置設定

經過以上的4.1~4.3三個步驟後,struct kmem_cache.node[]數組中對應的每個struct kmem_cache_node對象都配置設定好了(來自于kmem_cache_node.cpu_slab.page),下面要開始從struct kmem_cache kmem_cache.cpu_slab中配置設定struct kmem_cache對象,發現其cpu_slab.page和cpu_slab.partial都為空,這時要從對應的struct kmem_cache kmem_cache.node[nodeId].partial中挪用一個slab page,此時發現其也為空,這時就要調用new_slab()函數根據struct kmem_cache kmem_cache.size從buddy system 中配置設定一頁slab page,放入struct kmem_cache kmem_cache.node[nodeId].partial,然後再挪到cpu_slab.page中,這樣才能完成對struct kmem_cache類型對象的配置設定。

5. 代碼中部分字段注釋 檢視附件