1. 程式人生 > >linux核心資料結構---hash表

linux核心資料結構---hash表

連結串列雖然是最常見的資料結構,但實際使用中,由於連結串列的檢索能力較差,更多的是作為佇列和棧結構使用,如果需要查詢,比如通過pid查詢程序,通過描述符查詢inode,就需要用到檢索更快的資料結構——Hash表
先來看Hash節點的定義:

struct hlist_head {
	struct hlist_node *first;
};
struct hlist_node {
	struct hlist_node *next, **pprev;
};

其中 hlist_head 為頭結點,與連結串列不同的是還需要一個節點的概念 hlist_node,他們之間的關係如圖:


左側是定長陣列,每個節點是 hlist_head ,其中first指向 hlist_node 節點,hlist_node又組成一列連結串列,hlist_node的連結串列結構跟 list_head不同之處在於 hlist_node 的 pprev 指向了 前一個節點的指標地址。
Hash表

使用起來也非常簡單,首先通過hash函式計算目標值,得到一個索引,在hlist_head陣列中找到相應位置,再插入連結串列。Hash的連結串列Linux也提供了多種操作方式:

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h);
static inline void hlist_add_before(struct hlist_node *n, 
		struct hlist_node *next)
static inline void hlist_add_after(struct hlist_node *n, 
		struct hlist_node *next)
static inline void hlist_move_list(struct hlist_head *old,
		struct hlist_head *new);

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \     
        for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_entry(tpos, pos, head, member)                    \
        for (pos = (head)->first;                                        \
             pos &&                                                      \
                ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
             pos = pos->next)

INIT_HLIST_HEAD —— 初始化連結串列;
hlist_add_head —— 在連結串列頭插入節點;
hlist_add_before —— 在一個節點之前插入;
hlist_add_after —— 在一個節點之後插入;
hlist_entry —— 同list_entry;
hlist_for_each —— 相關的一系列函式進行連結串列的遍歷,與普通雙向連結串列操作相同。
除了Hash表中連結串列的操作,還有一個重要的元素是Hash函式,Hash函式的好壞直接影響Hash表的效能,Hash函式一般來講跟具體要實現的業務相關,include/linux/jhash.h下實現了幾個不同用途的Hash函式,另外,Linux還給出一個簡單的Hash函式:

static inline u32 hash_32(u32 val, unsigned int bits)
{
        /* On some cpus multiply is faster, on others gcc will do shifts */
        u32 hash = val * GOLDEN_RATIO_PRIME_32;

        /* High bits are more random, so use them. */
        return hash >> (32 - bits);
}

這個函式把32位整數Hash成bits位的整數,另外還有 hash_64 等等Hash函式。
最後再來通過一個簡單的例子看一下Hash表的使用:

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hash.h>

struct simple_hash
{
    int data;
    struct hlist_node node;
};

struct hlist_head * phash = NULL;

static int __init initialization(void)
{
    int i,k;
    struct hlist_head * phead;
    struct hlist_node * pnode;
    struct simple_hash * p;

    printk(KERN_INFO " init simple start\n");

    phash = (struct hlist_head*)kmalloc(sizeof(struct hlist_head) * 0xFF, GFP_KERNEL);
    for (i = 0; i < 0xFF; i++) {
        INIT_HLIST_HEAD(&phash[i]);
    }
    for (i = 0; i < 10; i++) {
        p = (struct simple_hash*)kmalloc(sizeof(struct simple_hash), GFP_KERNEL);
        k = i * 13;

        p->data = k;
        INIT_HLIST_NODE(&p->node);

        printk(KERN_INFO "insert %d\n", k);
        phead = &phash[hash_32(k, 8)];
        hlist_add_head(&p->node, phead);
    }
    k = 3 * 13;
    phead = &phash[hash_32(k, 8)];
    printk(KERN_INFO "search %d\n", k);
    hlist_for_each_entry(p, pnode, phead, node) {
        if (p->data == k) {
            printk(KERN_INFO " find it\n");
        }
    }

    printk(KERN_INFO "init simple end\n");
    return 0;
}

static void __exit cleanup(void)
{
    int i;
    struct hlist_head * phead = NULL;
    struct simple_hash * p = NULL;
    printk(KERN_INFO "cleanup simple\n");

    if (phash == NULL) {
        return;
    }

    for (i = 0; i < 0xFF; i++) {
        phead = &phash[i];
        while (!hlist_empty(phead)) {
            p = hlist_entry(phead->first, struct simple_hash, node);
            printk(KERN_INFO "delete %d", p->data);
            hlist_del(&p->node);
            kfree(p);
        }
    }
    kfree(phead);
}

module_init(initialization);
module_exit(cleanup);

MODULE_AUTHOR("cppbreak [email protected]");
MODULE_DESCRIPTION("A simple linux kernel module");
MODULE_VERSION("V0.1");
MODULE_LICENSE("Dual BSD/GPL");

相關推薦

linux核心資料結構---hash

連結串列雖然是最常見的資料結構,但實際使用中,由於連結串列的檢索能力較差,更多的是作為佇列和棧結構使用,如果需要查詢,比如通過pid查詢程序,通過描述符查詢inode,就需要用到檢索更快的資料結構——Hash表。 先來看Hash節點的定義: struct hlist_h

linux核心資料結構以及核心除錯

一、可移植性 1.1 資料型別可移植性 由於核心可能執行在不同的架構上,不同的架構具有不同的機器字長,因而可移植性對核心程式設計非常重要。核心資料使用的資料型別分為 3 個主要型別 標準C型別 明確大小的型別 用作特定核心物件的型別 1.1.1 標準 C 型別 使用標準

linux核心資料結構---連結串列(1)

Linux核心有一些基本的資料結構,這些資料結構是Linux實現的基礎,對於連結串列相信大家都不陌生,但是Linux核心中的連結串列與平常平常我們所使用的連結串列略有不同,第一次遇到或許會感到困惑。 先來看一個連結串列的節點,對於一個節點,分為兩部分,一部分是資料,另一

Linux核心資料結構

核心資料結構貫穿於整個核心程式碼中,這裡介紹4個基本的核心資料結構。 利用這4個基本的資料結構,可以在編寫核心程式碼時節約大量時間。 主要內容: 連結串列佇列對映紅黑樹 1. 連結串列 連結串列是linux核心中最簡單,同時也是應用最廣泛的資料結構。 核心中定義的是雙向連結串列。 1.1 標頭檔案簡介

[資料結構]Hash初學(開放定址法 )

/* Name:Hash表初學 (陣列實現連結串列 開放定址法 ) Actor:HT Time:2015年9月29日 Error Reporte: */ #include"stdio.h"

Linux 核心資料結構:點陣圖(Bitmap)

https://github.com/0xAX/linux-insides/blob/master/DataStructures/bitmap.md Data Structures in the Linux Kernel Bit arrays and bit op

資料結構hash】重排九宮…

【資料結構•hash表】重排九宮 Time Limit:10000MS  Memory Limit:65536K Total Submit:47 Accepted:15 Description 我先來說幾句: ==============================================

Linux核心設計與實現 總結筆記(第六章)核心資料結構

核心資料結構 Linux核心實現了這些通用資料結構,而且提倡大家在開發時重用。 核心開發者應該儘可能地使用這些資料結構,而不要自作主張的山寨方法。 通用的資料結構有以下幾種:連結串列、佇列、對映和二叉樹   一、連結串列 1.1 單向連結串列和雙向連結串列   1.2 環形

《深入Linux核心架構與底層原理》讀書筆記一——核心架構與核心資料結構知識

1、核心架構常見架構正規化:Linux核心上下層通訊方式橫向系統和縱向系統橫向系統如cgroup,proc,sys檔案系統,系統呼叫的組織,除錯系統,Core Dump,訊號,記憶體管理等;縱向系統是指具體的功能模組,如USB功能,一個對USB檔案的操作要走完核心中的很多個層

資料結構——線性的鏈式實現

 記錄一下比較完整的鏈式線性表的函式操作集 語言C++ 環境codeblocks17.01 #include <iostream> #include <stdio.h> #include <stdlib.h> #include <algor

資料結構-----------線性(下篇)之雙向連結串列

//----------雙向連結串列的儲存結構------------ typedef struct DuLNode { ElemType date; struct DoLNode *prior; struct DoLNode *next; } DoLNode,*DoLinkList;

資料結構---------------線性(下篇)之單鏈

單鏈表 特點:儲存空間不連續 結點(資料元素組成):資料域(儲存資料)和指標域(指標)A1 若用p來指向  則資料域為p->date   指標域為p->next 鏈式儲存結構: 單鏈表、迴圈連結串列、雙向連結串列根據連

資料結構------------線性(上篇)

線性表:由n(n>=0)個數據特性相同的元素構成的有限序列 線性表中的袁旭個數n(n>=0)定義為線性表的長度,n=0時為空表 非空的線性表或線性結構特點: 1)存在唯一的一個數被稱為“第一個”的資料元素; 2)存在唯一的一個數被稱為“最後一個”的資料元素; 3)除第

資料結構-順序(java)萌新寫的希望各位大佬提提意見

package bh.shy.list; import bh.shy.listconst.ListConst; public class MyList { // 定義一個數組T private T[] t; private int length; private int list

資料結構---線性的順序儲存結構

#include <stdio.h> #define TRUE 1 #define FALSE 0 #define MAXSIZE 19 typedef struct { int data[MAXSIZE]; int length; }linklist

資料結構-線性- 01 “兩個有序連結串列序列的合併” 問題

題目要求: 本題要求實現一個函式,將兩個連結串列表示的遞增整數序列合併為一個非遞減的整數序列。 函式介面定義: List Merge( List L1, List L2 ); 其中List結構定義如下: typedef struct Node *PtrToNode; struc

資料結構順序元素整體互換和單鏈插入練習

線性表用順序儲存,設計一個演算法,用盡可能少的輔助儲存空間將順序表中前m個元素與後n個元素進行整體互換,即將線性表 (a1,a2,a3,......,am,b1,b1,b3,......,bn)   (b1,b2,b3,.......,bn,a1,a2,a3,.......,am)

《java常用演算法手冊》 第二章 資料結構 順序 單鏈

資料元素:例如單向連結串列的一個節點  擁有 頭指標和資料兩個資料組成   四種儲存方式 :        

資料結構-線性(棧與佇列基本概念)

棧(stack,zhan):是限定僅在表尾進行插入和刪除操作的線性表。 把允許插入和刪除的一端稱為棧頂(top),另一端稱為棧底(bottom),不含任何資料元素的棧稱為空棧。棧又稱為後進先出(Last In First Out)的線性表,簡稱LIFO結構。 棧是一個線性表,棧元素

資料結構線性之鏈式儲存結構單鏈(C++)

一. 標頭檔案—linkedlist.h 1 #ifndef _LIKEDLIST_H_ 2 #define _LIKEDLIST_H_ 3 4 #include <iostream> 5 6 template <class T> 7 struc