linux核心的資料結構 2 雜湊表

2021-06-25 09:29:24 字數 2419 閱讀 6821

linux核心中實現了一種簡單的開雜湊表。雜湊表中的每個項都是乙個hlist_head結構,hlist_head結構是乙個鍊錶的頭結點,它的大小是固定的。而在鍊錶中具體儲存我們需要的資訊。這個鍊錶的實現方式與前文所講的雙向迴圈鍊錶有所不同。

linux核心中很多地方都使用了雜湊的技術,比如為了加快查詢特定pid的程序描述符的過程,linux核心中建立了乙個pid_hash的結構,類似的我們還可以發現tcp_hash,inode_hashtable等。

下面我們以pid_hash為例具體說明一下hash的實現細節:

pid_hash的宣告:

[cpp]view plain

copy

static

struct

hlist_head *pid_hash;  

[cpp]view plain

copy

struct

hlist_head ;  

我們可以看到pid_hash的每個項都是乙個煉表頭,大小為乙個指標的size,一般為4位元組。我們來看一下鍊錶節點的定義:

[cpp]view plain

copy

struct

hlist_node ;  

注意,這裡面向前的指標被定義為了struct hlist_node**型別,實際上pprev指標指向的是前乙個結點的next域。為什麼這樣設計呢?

原因:首先我們假設一下pprev域修改為struct hlist_node* prev,那麼現在這個鍊錶和我們平時所見到的一樣了。我們考慮下這個鍊錶的頭結點,它一定會是struct hilist_node型別的,否則第二個節點的prev無法指向它;而在看一下我們現在的實現,pprev只需要指向hlist_head型別的域即可(比如hlist_head中的first),這就意味著頭結點不需要是hlist_node型別。而hlist_node和hlist_head的區別在**,少了乙個指標!這就意味著節省了hash表的空間。

pid_hash的初始化是在核心初始化時(即start_kernel函式中)進行的,具體由pidhash_init函式完成。

[cpp]view plain

copy

void

__init pidhash_init(

void

)    

alloc_large_system_hash是專門用於為hash表分配一塊連續的記憶體的,引數4096意味著最多4k個項,也就是最多占用1頁的記憶體。init_hlist_head是負責鍊錶節點的初始化的巨集。

那麼下乙個重要的問題,hash函式是如何實現的呢?非常簡潔。

[cpp]view plain

copy

#define pid_hashfn(nr, ns)  \

hash_long((unsigned long

)nr + (unsigned 

long

)ns, pidhash_shift)  

其中,nr是pid的值,而ns表示的是pid的命名空間(這個是為了支援輕量級虛擬化而引入的新概念)。我們具體看一下hash_long的實現:

[cpp]view plain

copy

#if bits_per_long == 32

#define golden_ratio_prime golden_ratio_prime_32

#define hash_long(val, bits) hash_32(val, bits)

#elif bits_per_long == 64

#define hash_long(val, bits) hash_64(val, bits)

#define golden_ratio_prime golden_ratio_prime_64

#else

#error wordsize not 32 or 64

#endif

[cpp]view plain

copy

static

inline

u32 hash_32(u32 val, unsigned 

intbits)    

hash函式的原理就是乘乙個非常大的數golden_ratio_prime_32(會導致溢位),然後只使用了部分的高位最為結果。這個hash函式非常簡潔,計算很快。但是hash結果如何取決於golden_ratio_prime_32的選取。這個值接近**分割數會使得hash的結果最好。實際中linux核心選取了0x9e370001ul這個值,因為它接近**分割數並且容易計算0x9e370001ul =  2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1。

資料結構 雜湊表2(雜湊表)

開放位址法 當衝突發生時,通過查詢陣列中的乙個空位,並將資料確認,而不是用雜湊函式得到的陣列下標。缺點 將別的物件的位置占用了。別的物件儲存的時候只能查詢位置來存放。鏈位址法 在雜湊表每個單元中設定鍊錶,某個資料項的關鍵字還是像通常一樣對映到雜湊表的單元中,而資料項本身插入到單元的鍊錶中。建立乙個儲...

Linux核心中的資料結構與演算法(三)雜湊鍊錶

四,雜湊鍊錶 談到鍊錶就不得不談linux核心中另外乙個重要的結構,雜湊鍊錶。討論這個結構前,你需要對雜湊的最基本的概念要清楚哦,由於我們已經講過linux核心中的普通鍊錶的結構,這裡我們對比他們的區別來了解雜湊鍊錶會直觀一些。linux鍊錶認為雙指標表頭雙迴圈鍊錶對於hash表來說過於浪費,因而設...

資料結構 雜湊

裝填因子 key的個數與表長的比值。雜湊表查詢成功的平均查詢長度,查詢失敗的平均查詢長度都是期望,期望怎麼求,平均查詢長度就怎麼求。雜湊表這裡有兩種實現方式 線性開型定址雜湊,鍊錶雜湊。1.線性開型定址雜湊 陣列實現,資料個數不大於表長,放乙個元素時,若發生衝突,則順次線性掃瞄直到找到乙個空位。2....