Linux编程细节3-内核-数据结构

来源:互联网 发布:优化探究化学 编辑:程序博客网 时间:2024/06/07 15:52
内核代码版本:https://www.kernel.org/pub/linux/kernel/v2.6/   2.6.11

1 链表

http://blog.chinaunix.net/uid-26497520-id-3601869.html

1.1 概述

就像许多其他程序一样,操作系统内核经常需要维护数据结构的列表。有时,linux内核中同时存在多个链表的实现代码。为了减少重复代码的数量,内核开发者已经建立了一套标准的双向循环链表。如果你需要操作链表,那么建议你使用这一内核设施。内核链表的好主要体现为两点,一是可扩展性,内核一直都是在发展中的,所以代码都不能写成死代码,要方便修改和追加;二是封装,将链表常见的操作都进行封装,使用者只关注接口,不需关注实现。当使用这些链表接口时,应该始终牢记这些链表函数不进行任何锁定。如果你的程序有可能试图对同一个链表执行并发操作的话,则有责任实现一个锁方案。

1.2 头文件定义

源码版本:2.6.34.14版本内核源码

链表定义: include/linux/list.h

1.3 数据结构

《Linux内核设计与实现》读书笔记(六)- 内核数据结构

linux内核中的链表使用方法和一般数据结构中定义的链表是有所不同的。

1.3.1 传统的list


传统的链表有个最大的缺点就是不好共通化,因为每个node中的data1,data2等等都是不确定的(无论是个数还是类型)。
linux中的链表巧妙的解决了这个问题,linux的链表不是将用户数据保存在链表节点中,而是将链表节点保存在用户数据中。

1.3.2 内核的list



即将传统的”链表持有节点“改成现在的”节点持有链表“。这也即C++中的继承的含义

1.3.3 list_head结构

内核链表结构定义如下:

struct list_head {    struct list_head *next, *prev;};
为了在代码中使用内核链表设施,只需在构成链表的结构里面嵌入一个list_head。如:
struct todo_struct {        ... other members ...        struct list_head list;}

注意list_head的成员prev,next指向的是list_head而不是包含list_head的结构体,我们的链表是通过list_head链起来的

/* * ptr:是指向type中链表节点的指针。 * type:一般是个结构体,也就是包含用户数据和链表节点的结构体。  * member:则是type中定义链表节点是用的名字。*/#define list_entry(ptr, type, member) \container_of(ptr, type, member)#define container_of(ptr, type, member) ({\        const typeof( ((type *)0)->member ) *__mptr = (ptr);\        (type *)( (char *)__mptr - offsetof(type,member) );})  注:container_of定义于include/linux/kernel.h中#ifdef __compiler_offsetof#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)#else#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#endif注:offsetof定义于include/linux/stddef.h中

1.3.4 list_entry宏

第一次接触内核链表的同学可能会产生这样的疑问:得到list_head的地址有什么用,我们需要操作的是包含list_head的结构体,如何才能得到包含list_head的结构体的指针?这是list_entry将要完成的工作,相关源码如下:


举个例子来说明,我们获得了一个指向list_head的指针p,该list_head包含于todo_struct结构中并且其对应的成员名为list,那么list_entry(p, struct todo_struct, list)将返回该todo_struct的地址。从上面源码可以看出,offsetof计算成员在结构中的偏移,todo_struct的地址的计算方式就是用p的值减去list在todo_struct中的偏移。

下面分析一下container_of宏:

// 步骤1:将数字0强制转型为type*,然后取得其中的member元素((type *)0)->member  // 相当于((struct student *)0)->list// 步骤2:定义一个临时变量__mptr,并将其也指向ptr所指向的链表节点const typeof(((type *)0)->member)*__mptr = (ptr);// 步骤3:计算member字段距离type中第一个字段的距离,也就是type地址和member地址之间的差// offset(type, member)也是一个宏,定义如下:#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)// 步骤4:将__mptr的地址 - type地址和member地址之间的差// 其实也就是获取type的地址


1.3.5 list_for_each遍历宏

#define list_for_each(pos, head) \for (pos = (head)->next; prefetch(pos->next), pos != (head); \pos = pos->next)

1.3.6 list_head使用示例图


在使用链表时我们通常会定义一个链表头,链表头通常是一个独立的list_head结构,在使用链表前需初始化链表头。从上图可以看出,我们的链表几乎可以说是独立于包含它的结构的,任何保含list_head成员的结构都可以添加到链表中来(当然我们只会将相同的结构组合成一个链表),针对整个链表的插入,删除、和并等操作完全是在list_head上进行的,而链表的遍历也是在list_head上移动。由此,内核有一套通用的专门用于处理链表的函数和宏,我们不必针对每一个链表都写一套操作函数。

1.4 函数操作

下面将列出对链表进行操作的函数和宏(head指向为链表头):

1.4.1 初始化

LIST_HEAD_INIT(name) 在编译阶段初始化链表头

#define LIST_HEAD_INIT(name) { &(name), &(name) }


LIST_HEAD(name) 在编译阶段定义并初始化链表头

#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)


INIT_LIST_HEAD(struct list_head *head) 在运行阶段初始化链表头

#define INIT_LIST_HEAD(ptr) do { \(ptr)->next = (ptr); (ptr)->prev = (ptr); \} while (0)

1.4.2 增加、删除、替换、移动元素

list_add(struct list_head *new, struct list_head *head)list_add_tail(struct list_head *new, struct list_head *head)    将new插入到链表开始/末尾list_del(struct list_head *entry)list_del_init(struct list_head *entry)    从链表中删除entrylist_replace(struct list_head *old, struct list_head *new)list_replace_init(struct list_head *old, struct list_head *new)    在链表中用new替换oldlist_move(struct list_head *list, struct list_head *head)list_move_tail(struct list_head *list, struct list_head *head)    将list从其所在链表删除,再插入到head链表的开始/末尾list_rotate_left(struct list_head *head)    将链表第一元素移动到最后

1.4.3 链表判定

list_is_last(const struct list_head *list, const struct list_head *head)    判定list为是否链表最后一个元素list_empty(const struct list_head *head)list_empty_careful(const struct list_head *head)    判定链表是否为空list_is_singular(const struct list_head *head)    判定链表是否只包含一个元素

1.4.4 合并、分割链表

list_cut_position(struct list_head *head1, struct list_head *head2, struct list_head *entry)    将head链表一分为二

list_splice(const struct list_head *head1, struct list_head *head2)list_splice_init(struct list_head *head1, struct list_head *head2)    合并两个链表(这是为栈设计的)

list_splice_tail(struct list_head *head1, struct list_head *head2)list_splice_tail_init(struct list_head *list, struct list_head *head)    合并两个链表(这是为队列设计的)

1.4.5 访问包含list_head的结构体

list_entry(entry, type, member)    将一个list_head结构指针映射回指向包含它的大结构的指针list_first_entry(head, type, member)    返回链表第一项(包含list_entry的结构体)

1.4.6 遍历

list_for_each(pos, head)list_for_each_safe(pos, n, head)     顺序遍历链表,遍历过程中,pos指向list_head结构体list_for_each_prev(pos, head)list_for_each_prev_safe(pos, n, head)    逆序遍历链表,遍历过程中,pos指向list_head结构体list_for_each_entry(pos, head, member)    顺序遍历链表,遍历过程中,pos指向包含list_head的大结构体list_for_each_entry_reverse(pos, head, member)    逆序遍历链表,遍历过程中,pos指向包含list_head的大结构体list_prepare_entry(pos, head, member)list_for_each_entry_continue(pos, head, member)list_for_each_entry_continue_reverse(pos, head, member)list_for_each_entry_from(pos, head, member)list_for_each_entry_safe(pos, n, head, member)list_for_each_entry_safe_continue(pos, n, head, member)list_for_each_entry_safe_reverse(pos, n, head, member)

1.5 示例代码

<list.c>
/* 练习使用linux内核链表,功能包括: * 定义链表结构,创建链表、插入节点、删除节点、移动节点、遍历节点 **/#include <stdio.h>#include <inttypes.h>#include <stdlib.h>#include <errno.h>#include "list.h"//定义STUDENT链表结构typedef struct student{    uint32_t    id;    char        sex;    struct      list_head list;//链表节点}STUDENT;STUDENT* set_student_info(uint32_t id, char sex){    STUDENT* stu = (STUDENT*)malloc(sizeof(STUDENT));    if (stu == NULL)    {        fprintf(stderr, "Failed to malloc memory, errno:%u, reason:%s\n", errno, strerror(errno));        return NULL;    }    stu->id = id;    stu->sex = sex;    return stu;}static void for_each_student(const struct list_head* head){    struct list_head* pos;    STUDENT* stu;    //遍历链表    list_for_each(pos, head)    {        stu = list_entry(pos, STUDENT, list);        printf("id: %u\tsex: %c\t\n",stu->id, stu->sex);    }}void destroy_student_list(struct list_head* head){    struct list_head* pos = head->next;    struct list_head* tmp = NULL;    while (pos != head)    {        tmp = pos->next;        list_del(pos);        pos = tmp;    }}int main(){    //声明并初始化一个链表头结构head    LIST_HEAD(head);    //插入三个STUDENT    STUDENT* stu;    stu = set_student_info(1001,'f');    list_add_tail(&stu->list, &head);    stu = set_student_info(1002,'m');    list_add_tail(&stu->list, &head);    stu = set_student_info(1003,'f');    list_add_tail(&stu->list, &head);    printf("After insert three STUDENT: \n");    for_each_student(&head);    //将第一个节点移到末尾    printf("Move first node to tail:\n");    list_move_tail(head.next, &head);    for_each_student(&head);    //删除最后一个节点    printf("Delete the last node:\n");    list_del(head.prev);    for_each_student(&head);    destroy_student_list(&head);    return 0;}
<list.h>
#ifndef _LINUX_LIST_H#define _LINUX_LIST_H#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ( { \        const typeof( ((type *)0)->member ) *__mptr = (ptr); \        (type *)( (char *)__mptr - offsetof(type,member) ); } )static inline void prefetch(const void *x) {;}static inline void prefetchw(const void *x) {;}#define LIST_POISON1  ((void *) 0x00100100)#define LIST_POISON2  ((void *) 0x00200200)struct list_head {        struct list_head *next, *prev;};#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD(name) \        struct list_head name = LIST_HEAD_INIT(name)#define INIT_LIST_HEAD(ptr) do { \        (ptr)->next = (ptr); (ptr)->prev = (ptr); \} while (0)/** 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;}/*** list_add - add a new entry* @new: new entry to be added* @head: list head to add it after** Insert a new entry after the specified head.* This is good for implementing stacks.*/static inline void list_add(struct list_head *new, struct list_head *head){        __list_add(new, head, head->next);}/*** 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);}static inline void __list_del(struct list_head * prev, struct list_head * next){        next->prev = prev;        prev->next = next;}static inline void list_del(struct list_head *entry){        __list_del(entry->prev, entry->next);        entry->next = LIST_POISON1;        entry->prev = LIST_POISON2;}static inline void list_del_init(struct list_head *entry){        __list_del(entry->prev, entry->next);        INIT_LIST_HEAD(entry);}static inline void list_move(struct list_head *list, struct list_head *head){        __list_del(list->prev, list->next);        list_add(list, head);}static inline void list_move_tail(struct list_head *list,                                  struct list_head *head){        __list_del(list->prev, list->next);        list_add_tail(list, head);}static inline int list_empty(const struct list_head *head){        return head->next == head;}static inline int list_empty_careful(const struct list_head *head){        struct list_head *next = head->next;        return (next == head) && (next == head->prev);}static inline void __list_splice(struct list_head *list,                                 struct list_head *head){        struct list_head *first = list->next;        struct list_head *last = list->prev;        struct list_head *at = head->next;        first->prev = head;        head->next = first;        last->next = at;        at->prev = last;}/*** list_splice - join two lists* @list: the new list to add.* @head: the place to add it in the first list.*/static inline void list_splice(struct list_head *list, struct list_head *head){        if (!list_empty(list))                __list_splice(list, head);}/*** list_splice_init - join two lists and reinitialise the emptied list.* @list: the new list to add.* @head: the place to add it in the first list.** The list at @list is reinitialised*/static inline void list_splice_init(struct list_head *list,                                    struct list_head *head){        if (!list_empty(list)) {                __list_splice(list, head);                INIT_LIST_HEAD(list);        }}#define list_entry(ptr, type, member) container_of(ptr, type, member)#define list_for_each(pos, head) \        for (pos = (head)->next; prefetch(pos->next), pos != (head); \                pos = pos->next)#define __list_for_each(pos, head) \        for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_prev(pos, head) \        for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \                pos = pos->prev)#define list_for_each_safe(pos, n, head) \        for (pos = (head)->next, n = pos->next; pos != (head); \                pos = n, n = pos->next)#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))#define list_for_each_entry_reverse(pos, head, member)                        \        for (pos = list_entry((head)->prev, typeof(*pos), member);        \             prefetch(pos->member.prev), &pos->member != (head);         \             pos = list_entry(pos->member.prev, typeof(*pos), member))#define list_prepare_entry(pos, head, member) \        ((pos) ? : list_entry(head, typeof(*pos), member))#define list_for_each_entry_continue(pos, head, member)                 \        for (pos = list_entry(pos->member.next, typeof(*pos), member);        \             prefetch(pos->member.next), &pos->member != (head);        \             pos = list_entry(pos->member.next, typeof(*pos), member))#define list_for_each_entry_safe(pos, n, head, member)                        \        for (pos = list_entry((head)->next, typeof(*pos), member),        \                n = list_entry(pos->member.next, typeof(*pos), member);        \             &pos->member != (head);                                         \             pos = n, n = list_entry(n->member.next, typeof(*n), member))//HASH LISTstruct hlist_head {        struct hlist_node *first;};struct hlist_node {        struct hlist_node *next, **pprev;};#define HLIST_HEAD_INIT { .first = NULL }#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)static inline int hlist_unhashed(const struct hlist_node *h){        return !h->pprev;}static inline int hlist_empty(const struct hlist_head *h){        return !h->first;}static inline void __hlist_del(struct hlist_node *n){        struct hlist_node *next = n->next;        struct hlist_node **pprev = n->pprev;        *pprev = next;        if (next)                next->pprev = pprev;}static inline void hlist_del(struct hlist_node *n){        __hlist_del(n);        n->next = LIST_POISON1;        n->pprev = LIST_POISON2;}static inline void hlist_del_init(struct hlist_node *n){        if (n->pprev)  {                __hlist_del(n);                INIT_HLIST_NODE(n);        }}static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h){        struct hlist_node *first = h->first;        n->next = first;        if (first)                first->pprev = &n->next;        h->first = n;        n->pprev = &h->first;}/* next must be != NULL */static inline void hlist_add_before(struct hlist_node *n,                                        struct hlist_node *next){        n->pprev = next->pprev;        n->next = next;        next->pprev = &n->next;        *(n->pprev) = n;}static inline void hlist_add_after(struct hlist_node *n,                                        struct hlist_node *next){        next->next = n->next;        n->next = next;        next->pprev = &n->next;        if(next->next)                next->next->pprev  = &next->next;}#define hlist_entry(ptr, type, member) container_of(ptr,type,member)#define hlist_for_each(pos, head) \        for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \             pos = pos->next)#define hlist_for_each_safe(pos, n, head) \        for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \             pos = n)#define hlist_for_each_entry(tpos, pos, head, member)                         \        for (pos = (head)->first;                                         \             pos && ({ prefetch(pos->next); 1;}) &&                         \                ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \             pos = pos->next)#define hlist_for_each_entry_continue(tpos, pos, member)                 \        for (pos = (pos)->next;                                                 \             pos && ({ prefetch(pos->next); 1;}) &&                         \                ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \             pos = pos->next)#define hlist_for_each_entry_from(tpos, pos, member)                         \        for (; pos && ({ prefetch(pos->next); 1;}) &&                         \                ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \             pos = pos->next)#define hlist_for_each_entry_safe(tpos, pos, n, head, member)                  \        for (pos = (head)->first;                                         \             pos && ({ n = pos->next; 1; }) &&                                  \                ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \             pos = n)#endif


2 队列

2.1 概述

内核中的队列是以字节形式保存数据的环形缓冲区,所以获取数据的时候,需要知道数据的大小。

内核队列编程需要注意的是:
1 队列的size在初始化时,始终设定为2的n次方
2 使用队列之前将队列结构体中的锁(spinlock)释放

2.2 头文件定义

源码版本:2.6.34.14版本内核源码

链表定义: include/linux/kfifo.h

2.3 数据结构

2.3.1 kfifo结构

struct kfifo {unsigned char *buffer;/* the buffer holding the data */unsigned int size;/* the size of the allocated buffer */unsigned int in;/* data is added at offset (in % size) */unsigned int out;/* data is extracted from off. (out % size) */spinlock_t *lock;/* protects concurrent modifications */};

2.4 操作函数

//根据给定buffer创建一个kfifovoid kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)//给定size分配buffer和kfifoint kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)//释放kfifo空间void kfifo_free(struct kfifo *fifo)//向kfifo中添加数据unsigned int kfifo_in(struct kfifo *fifo, const void *from,unsigned int len)//从kfifo中取数据unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)//获取kfifo中有数据的buffer大小unsigned int kfifo_len(struct kfifo *fifo)
在kfifo_init和kfifo_calloc中,kfifo->size的值总是在调用者传进来的size参数的基础上向2的幂扩展,这是内核一贯的做法。这样的好处不言而喻--对kfifo->size取模运算可以转化为与运算,如:kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)。kfifo的巧妙之处在于in和out定义为无符号类型,在put和get时,in和out都是增加,当达到最大值时,产生溢出,使得从0开始,进行循环使用。

例如下图:蓝色表示空闲,红色表示占用。
(1)空的kfifo


(2)put一个buffer后


(3)get一个buffer后


(4)当此时put的buffer长度超出in到末尾长度时,则将剩下的移到头部去



3 映射

3.1 概述

映射的有点想其他语言(C#或者python)中的字典类型,每个唯一的id对应一个自定义的数据结构。映射的使用需要注意的是,给自定义的数据结构申请一个id的时候,不能直接申请id,先要分配id(函数idr_pre_get),分配成功后,在获取一个id(函数idr_get_new)。

3.2 头文件定义

源码版本:2.6.34.14版本内核源码
链表定义: include/linux/idr.h

3.3 未整理

idr的结构比较复杂,我也没有很好的理解,但是csdn上有篇介绍linux idr结构的博客写的挺好,图文并茂:http://blog.csdn.net/paomadi/article/details/8539794


4 红黑树

4.1 概述

红黑树由于节点颜色的特性,保证其是一种自平衡的二叉搜索树。
红黑树的一系列规则虽然实现起来比较复杂,但是遵循起来却比较简单,而且红黑树的插入,删除性能也还不错。所以红黑树在内核中的应用非常广泛,掌握好红黑树,即有利于阅读内核源码,也可以在自己的代码中借鉴这种数据结构。

红黑树必须满足的规则:
1,所有节点都有颜色,要么红色,要么黑色
2,根节点是黑色,所有叶子节点也是黑色
3,叶子节点中不包含数据
4,非叶子节点都有2个子节点
5,如果一个节点是红色,那么它的父节点和子节点都是黑色的
6,从任何一个节点开始,到其下叶子节点的路径中都包含相同数目的黑节点
红黑树中最长的路径就是红黑交替的路径,最短的路径是全黑节点的路径,再加上根节点和叶子节点都是黑色,从而可以保证红黑树中最长路径的长度不会超过最短路径的2倍。
 

4.2 头文件定义

源码版本:2.6.34.14版本内核源码
链表定义: include/linux/rbtree.h


4.3 未整理

http://www.cnblogs.com/wang_yb/archive/2013/04/16/3023892.html


0 0