核心 address space與基樹

2021-09-08 02:30:30 字數 3559 閱讀 9531

看了半天也看不懂……好像有點明白了:有時候看原始碼最讓人頭疼的不是它的語言或者邏輯,而是它所使用的演算法。

先mark下來,先研究下基樹再來看這篇文章。

**

struct address_space __attribute__((aligned(sizeof(long))));

struct address_space_operations ;

而頁快取記憶體如果頁數太多,而只用順序查詢的方式,那效率一定是低下的,此時就用了乙個搜尋樹方式,其中每個address_space都對應一顆搜尋樹。而結構體中字段page_tree對應的就是基樹的根:

struct radix_tree_root ;

struct radix_tree_node ;

根結構中的radix_tree_node對應的是樹的乙個節點。

#ifdef __kernel__

#define radix_tree_map_shift    6

#else

#define radix_tree_map_shift    3       /* for more stressful testing */

#endif

#define radix_tree_tags         2

#define radix_tree_map_size     (1ul << radix_tree_map_shift)

#define radix_tree_map_mask     (radix_tree_map_size-1)

#define radix_tree_tag_longs    \

((radix_tree_map_size + bits_per_long - 1) / bits_per_long)

而通過基樹的查詢到底是如何進行的呢?比如現在有乙個第二層節點下的page,如果從根開始找,那麼就要先找到page所在的第二層節點,而第二層節點對應的是第一層節點的乙個陣列下標。那麼方法就是當只有一層的時候,那麼陣列下標就是所需要的page.也就是頁索引的低6位是需要的slot的陣列下標。可是當擁有兩層的時候,那麼頁索引的低12位分成兩部分,高位是找到page所在的節點在上層節點中的slot陣列下標。而低位部分則是第二層節點的slot陣列下標,如此就可以通過兩次找到page.當是6層的時候,頁索引的最高兩位表示第一層節點陣列的下標,接下來的6位表示第二層節點陣列的下標,這樣一直到最低的6位,他們表示第六層節點陣列的下標。以上就是基樹搜尋的思想。

而關於頁快取記憶體的還有幾個常用處理函式:查詢頁,增加頁,刪除頁更新頁。

remove_from_page_cache(struct page *page)

以上就是查詢頁相關的函式。查詢乙個頁面使用的就是上面所言的搜尋樹方式。幾個函式也大同小異。

void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)

return *slot;

}在此函式中,重點是依靠radix_tree_lookup函式來完成的,他的目的找到乙個頁。他的兩個引數也是address_space物件和偏移量。在radix函式中,首先就是要驗證索引是否大於樹的深度,如果大於,那麼肯定找不到目標頁。函式中的shift始終是乙個重點,因為他決定了每次找尋的步移。slot是記錄節點下面各個元素的。從根開始。逐步按照層次推進尋找。(*slot)->slots + ((index >> shift) & radix_tree_map_mask)這段就是上面談到的,找到在節點上的page,也就是page所對應節點的slot陣列下標。而find_get_pages函式則是實現在快取記憶體中查詢一組具有相鄰索引的頁。比如函式中多出的幾個引數:start(位址空間中相對於搜尋起始位置的偏移量),nr_pages(所檢索到頁的最大數量),**pages(指向該函式賦值的頁描述符陣列的指標)。在函式中,那個for迴圈體中的ret,就是找到的頁數。而具體實現過程,則是依靠函式radix_tree_gang_lookup來完成的。函式很簡單。作用就是實現在快取記憶體中查詢一組具有相鄰索引的頁。

unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results,unsigned long first_index, unsigned int max_items)

return ret;

}radix_tree_preload_end();

}return error;

}在radix_tree_preload函式中,關鍵的一句是:node = kmem_cache_alloc(radix_tree_node_cachep, gfp_mask);其中這個node結構就是struct radix_tree_node *node型別。這個函式以前談過是快取記憶體申請函式。是slab分配器中的,分陪的是slab物件。再返回函式add_to_page_cache,如果分配成功,則就要插入這個節點。radix_tree_insert函式的作用就是。他的三個引數的功能就是傳入插入點,插入物件,以及offset.在這個函式中,首先是判斷的是如果可以插入到當前深度,那麼就插入,否則在去尋找定位。

if ((!index && !root->rnode) || index > radix_tree_maxindex(root->height))

其中的函式radix_tree_maxindex的作用就是獲得最大索引,這個判斷的目的就在於,如果當前節點超過了樹本身具有的深度,那麼就要再加一層。函式radix_tree_extend的作用就是通過增加適當數量的節點來增加樹的深度。如果這步不存在,那麼就按部就班的進行:

slot = &root->rnode;

height = root->height;

shift = (height-1) * radix_tree_map_shift;

offset = 0; 

在迴圈體中,從根節點開始遍歷,直到最深的一層。如果根節點的slot為null,那麼就要申請乙個,在radix_tree_node_alloc函式中,重要的一句就是:ret = kmem_cache_alloc(radix_tree_node_cachep, root->gfp_mask);在迴圈體後面就是將頁找到適當的位置。然後返回0.

while (height > 0)

offset = (index >> shift) & radix_tree_map_mask;

node = *slot;

slot = (struct radix_tree_node **)(node->slots + offset);

shift -= radix_tree_map_shift;

height--;

}如果函式成功返回0,那麼在add_to_page_cache函式中,就開始設定。然後呼叫radix_tree_preload_end()。重新啟用核心搶占。

if (!error)

刪除頁的函式,remove_from_page_cache的作用就是從快取記憶體中刪除頁描述符。可見最後落實的函式是radix_tree_delete.

void __remove_from_page_cache(struct page *page)

void remove_from_page_cache(struct page *page)

核心 address space與基樹

看了半天也看不懂 好像有點明白了 有時候看原始碼最讓人頭疼的不是它的語言或者邏輯,而是它所使用的演算法。先mark下來,先研究下基樹再來看這篇文章。struct address space attribute aligned sizeof long struct address space oper...

linux核心 address space 結構

看linux核心很容易被struct address space 這個結構迷惑,它是代表某個位址空間嗎?實際上不是的,它是用於管理檔案 struct inode 對映到記憶體的頁面 struct page 的 與之對應,address space operations 就是用來操作該檔案對映到記憶體...

胖核心與瘦核心

現代人的日常生活中有兩個無處不在的符號,那就是 胖 和 瘦 由於漢字的獨特作用,潛移默化地加深了中國人對 胖 和 瘦 的下意識的評價。以 疒 和 叟 造成了 瘦 是一直給中國人提供了長 胖 的動力,試想,誰又想得病或者是擁有 老叟 的身體呢?其次,以 肉 為部首造成了 胖 這也是第三個動力所在 人體...