内核---内核基础层的数据结构

来源:互联网 发布:中国空军臂章淘宝 编辑:程序博客网 时间:2024/06/05 14:32

内核使用的数据结构有 双向链表、 hash链表 和 单项链表; 另外 红黑树 和 基树(redix树) 也是内核使用的数据结构。 实际上,这也是程序代码中通常使用的数据结构。

container是Linux中很重要的一个概念,实现container能实现对象的 封装。

代码如下:

#define container_of(ptr, type, member)({

const typeof( ((type *) 0)->member ) *__mptr = (ptr);

(type *) ( (char *)__mptr - offsetof(type, member) ); })

这个方法巧妙的实现了通过一个结构的一个成员找到整个结构的地址。内核中大量使用了这个方法。


1、双向链表:

list是双向链表的一个抽象,他定义在/include/linux目录下。首先看看list的结构定义:

struct list_head {

struct list_head *next, *prep;

};

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

list库提供的list_entry使用了container,通过container可以从list找到整个数据对象,这样list就成为一种通用的数据结构:

#define list_entry(ptr, type, member)

container_of(ptr, type, member)

内核定义了很多对list结构操作的内联函数和 宏:

*LIST_HEAD 定义并初始化一个list链表

*list_add_tail 加一个成员到链表尾

*list_del 删除一个list成员

*list_empty 检查链表是否为空

*list_for_each 遍历链表

*List_for_each_safe:遍历链表,和list_for_each的区别是是否可以删除遍历的成员

*list_for_each_entry:遍历链表,通过container方法返回结构指针


include/linux/List.h

LIST_HEAD:

#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)

struct list_head {struct list_head *next, *prev;};

--初始化一个链表,next 和 prev指针都指向 name结构的地址;

很多情况下 LIST_HEAD ,name都是命名的链表名

例如:

//------ /device/pci/Probe.c/* Ugh.  Need to stop exporting this to modules. */LIST_HEAD(pci_root_buses);EXPORT_SYMBOL(pci_root_buses);LIST_HEAD(pci_devices);

        在此例中,初始化链表头:

pci_root_buses :所有已知的PCI总线的list;

pci_device :所有 PCI设备的list;

//------/include/linux/Pci.h/* Do NOT directly access these two variables, unless you are arch specific pci * code, or pci core code. */extern struct list_head pci_root_buses;/* list of all known PCI buses */extern struct list_head pci_devices;/* list of all devices */

 以上的例子做过总结:

LIST_HEAD(pci_root_buses) 

---> struct list_head pci_root_buses= LIST_HEAD_INIT(pci_root_buses 

---> struct list_head pci_root_buses = { &pci_root_buses, &pci_root_buses }

---> struct list_head pci_root_buses = { next = &pci_root_buses ,prev = &pci_root_buses }

       a

list_add_tail :

/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. This is useful for implementing queues. */static inline void list_add_tail(struct list_head *new, struct list_head *head){__list_add(new, head->prev, head);}

        其中调用__list_add 函数:

/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */static inline void __list_add(struct list_head *new,      struct list_head *prev,      struct list_head *next){next->prev = new;new->next = next;new->prev = prev;prev->next = new;}

        a

/** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */static inline void list_del(struct list_head *entry){__list_del(entry->prev, entry->next);entry->next = LIST_POISON1;entry->prev = LIST_POISON2;}

        a

/** * list_empty - tests whether a list is empty * @head: the list to test. */static inline int list_empty(const struct list_head *head){return head->next == head;}

        a

/** * list_for_each-iterate over a list * @pos:the &struct list_head to use as a loop cursor. * @head:the head for your list. */#define list_for_each(pos, head) \for (pos = (head)->next; prefetch(pos->next), pos != (head); \        pos = pos->next)

        a

/** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos:the &struct list_head to use as a loop cursor. * @n:another &struct list_head to use as temporary storage * @head:the head for your list. */#define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)

        a

/** * list_for_each_entry-iterate over list of given type * @pos:the type * to use as a loop cursor. * @head:the head for your list. * @member:the name of the list_struct within the struct. */#define list_for_each_entry(pos, head, member)\for (pos = list_entry((head)->next, typeof(*pos), member);\     prefetch(pos->member.next), &pos->member != (head); \     pos = list_entry(pos->member.next, typeof(*pos), member))

        a

        a

        a

2、hash链表

hash链表和双向链表很相似,适用于hash表。has链表的头部定义:

struct hlist_head {

struct hlist_node *first;

};

和通常的list链表比较,hlist只有一个指针,这样就节省了一个指针的内存。如果hash表非常庞大,每个hash表节省一个指针,整个hash表节省的内存十分可观。这就是内核中专门定义hash list的原因。

hash_lsit库提供的函数和list相似,具体情况如下:

*HLIST_HEAD 定义并初始化一个hash_list链表头

*hlist_add_del 加一个成员到hash链表头

*hlist_del 删除一个hash链表成员

*hlist_empty 检查hash链表是否为空

*hlist_for_each 遍历hash链表

*hlist_for_each_safe: 遍历hash链表,和hlist_for_each的区别是可以删除遍历的成员

*hlist_for_each_etry: 遍历hash链表,通过container方法返回结构体指针

HLIST_HEAD的定义如下:

#define HLIST_HEAD(name) structhlist_head name = {  .first = NULL }

#define HLIST_HEAD_INIT { .first = NULL }#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

HLIST_HEAD(name)     --定义初始化

INIT_HLIST_HEAD(ptr) --初始化  已定义的hlist链表

HLIST_HEAD_INIT        --

1>>eg

struct hlist_head my_hlist = HLIST_HEAD_INIT

调用HLIST_HEAD_INITmy_hlist哈希表头结点只进行初始化,将表头结点的fist指向空

2>>name

name 为结构体 ------>struct hlist_head{}的一个结构体变量

3>>HLIST_HEAD(name)

  声明且进行初始化

HLIST_HEAD(my_hlist) ------调用HLIST_HEAD函数宏对my_hlist哈希表头结点进行声明并进行初始化。将表头结点的fist指向空

4>>OK

HLIST_HEAD          :宏在编译静态初始化;

INIT_HLIST_HEAD :在运行时进行初始化

INIT_HLIST_HEAD(&my_hlist) ------调用INIT_HLIST_HEAD俩将my_hlist进行初始化,将其first域指向空即可

a

3、单项链表

内核中没有单项链表的定义。实际上,很多地方上使用了单向链表的概念,代码清单如下;

-------------------------------------------------------------------------------------------

for( i=0, p-= n ;  i < n ; i++, p++, index++) {


}

-------------------------------------------------------------------------------------------

上面的例子是是字符设备的map表,probe结构其实就是单向链表。这种结构在内核中使用十分广泛。

4、红黑树

5、基树(radix树)

内核提供了一个radix树库,代码在/lib/radix-tree.c文件。radix树是一种空间换时间的数据结构,通过空间的冗余减少时间上的消耗。

图暂缺

如图所示,元素空间总数为256,但元素个数不固定。如果用数组存储,好处是入口查找只要用一次操作,但存储空间需要256。 如果用链表存储,存储树,第一级最多16个冗余成员,代表元素前16位的索引,第二级代表元素后16位的索引。只要两级查找,就可以找到特定的元素,而且只有少量的冗余数据。

图中假设只有一个元素10001000,那么只有树的第一级有元素,而且树的第二级只有1000这个节点有数据,其他的节点不必分配空间。这样既可以快速定位查找,也减少了冗余数据。

radix树很适合稀疏的数据,内核中文件的页缓存就采用了radix树。关于radix树的文章很多,读者可以结合内核radix代码树的实现代码分析一下。



原创粉丝点击