list.h 解析

                                           内核数据结构list.h 解析

首先这个数据结构不同于我们我们设计的链表结构。

其实链表这种结构的各种操作就是改变链表的指针域而已,所以可以发现内核的链表结构在设计的时候其实只有指针域。

我的内核版本: 4.0.8-200.fc21.x86_6  此篇博客所有的源码都来自这个版本的内核。

节点的定义在./include/types.h 中

 

struct list_head {
    struct list_head *next,*prev;
};
 其实就是:

struct list_head{

    struct list_head *next;
    struct list_head *prev;

};
可以看到链表的核心其实就是指针域的改变。

下面是两个头结点的初始化宏。

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

#define LIST_HEAD(name)\
        struct list_head name = LIST_HEAD_INIT(name)
这两句是源代码的头两个宏定义,作用是初始化一个链表节点的链接
例如申请一个变量head作为头结点
    LIST_HEAD(head) 等价于struct list_head head = {&(head),&(head)};
    就是对头节点的赋值了


同样有一个函数可以初始化

static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}
在已知的两个节点中插入一个节点,此处是已经知道这两个节点的情况:
static inline void __list_add(strcut list_head *new,strcut list_head *prev,
                              strcut list_head *next)
{
    next->prev = new;
    new->next  = next;
    new->prev  = prev;
    prev->next = new;
}
有了这个添加一个节点的函数,我们基本可以添加元素到任意位置了
例如添加一个节点进头结点之后
static inline void list_add_tail(struct list_head *new,
                                struct list_head *head)
{
    __list_add(new,head,head->next);
}
添加一个节点到头指针之前

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_entry(struct list_head *entry)
{
    __list_del(entry->prev,entry->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_replace(struct list_head *old,
                               struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}

使用一个新的节点替换一个旧的节点并且将旧的节点置空

static inline void list_replace_init(struct list_head *old,
                                    struct list_head *new)
{
    list_replace(old,new);
    INIT_LIST_HEAD(old);
}

删除一个结点从链表中,并且重新初始化

static inline void list_del_init(struct list_head *entry)
{
    __list_del_entry(entry);
    INIT_LIST_HEAD(entry);
}


删除一个节点并且在这个位置上增加一个其它的节点

static inline void list_move(struct list_head *list,
                            struct list_head *head)
{
    __list_del_entry(list);
    list_add(list,head);
}
//删除一个节点的前一个节点,并且插入一个新节点

static inline void list_move_tail(struct list_head *list
                                 struct list_head *head)
{
    __list_del_entry(list);
    list_add_tail(list,head);
}
/测试一个节点是不是其头节点

static inline int list_is_last(const struct list_head *list,
                               const struct list_head *head)
{
    return list->next == 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->next);
}

反转一个节点

static inline void list_roate_left(struct list_head *head)
{
    struct list_head *first;
    if(!list_empty(head)){
        first = head->next;
        list_move_tail(first,head);
    }
}

测试一个链表是不是仅仅只有一个节点

static inline int list_is_singular(const struct list_head *head)
{
    return !list_empty(head) && (head->next == head->prev);
}

链表分割函数一共有三个参数,list :新的链表头结点;head:裁剪剩下的链表
entry:包含在链表head内并且是开始裁剪的结尾

static inline void __list_cut_position(struct list_head *list,
                struct list_head *head,struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}

一个分割链表的实际操作

static inline void list_cut_position(struct list_head *list,
                    struct list_head *head,struct list_head *entry)
{
    if(list_empty(head))
        return;
    if(list_is_singular(head) && (head->next != entry && head != entry))
        return ;
    if(entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list,head,entry);

}
合并操作,这里主要看下核心步骤,就是将list领头的这个链表插入到
prev 和 next 之间不包含list 节点

static inline void __list_splice(const struct listg_head *list,
                                struct list_head *prev,
                                struct list_head *next)
    struct list_head *first = list->next;
    struct list_head *last  = list->prev;

    first->prev = prev;
    prev->next  = first;
    last->next = next;
    next->prev = last;

}

static inline void list_splice(const struct list_head *list,
                              struct list_head *head)
{
    if(!list_empty(list)){
        __list_splice(list,head,head->next);
    }
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

链表的宏遍历再说这个之前,需要看几个必须的宏,不然,难以理解,

这是通过一个已经知道的指针地址,得到这个结构体的首地址,这样做封装性更好,适用范围更广。

#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)) ; })

offsetof 的代码还在下边

#define offsetof(type, member)     ((unsigned int )&((type *)0)->member)
一般人看到这样的C代码肯定是一头雾水难以理解这是为什么,甚至根本看不明白是什么意思,毕竟是内核代码,看不懂也是正常的。首先第一个宏有三个参数  list_head *   指针,最终它会是这个链表节点的首地址,就可以通过访问它的其它成员变量了。  ptr 传入时的类型,也就是list_head * 这个结构体中    list_head  的变量名<其次是一个运算符 typeof 这是 C 语言的扩展。可以得到一个结构体的类型。((type *)0)->member ) * __mptr  = (ptr); 
这一句,就是声明一个type 的member 类型的指针 __mptr  并且赋值ptr.

#define offsetof(type, member)     ((unsigned int )&((type *)0)->member)

这个宏可以得到member 相对于结构体首地址的相对地址。

(type *)((char *)__mptr  - offsetof(type, member)) ; })
这一句,就可以得到这个结构体的首地址了,就是地址相减,用member的地址减去它相对于首地址的偏移量就是这个结构体的首地址。


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

这里ptr是一个链表的头结点,这个宏就是取得这个链表第一个元素的所指结构体的首地址

#define list_first_entry(ptr,type,member) \
        list_entry((ptr)->next,type,member)


判断一个链表是否为空,且取首地址。

#define list_first_entry_or_null(ptr,type,member) \
        (!list_empty(ptr) ? list_first_entry(ptr,type,member):null)

也可以根据变量的next与prev来确定结构体的地址

#define list_prev_entry(pos,member) \
        list_entry((pos)->member.prev,typeof(*(pos)),member)
#define list_next_entry(pos,member) \
        list_entry((pos)->member.prev,typeof(*(pos)),member)


链表的遍历
#define list_for_each(pos,head)\
    for(pos = (head)->next; pos != (head);pos = pos->next)

遍历一遍链表,使用entry方法

#define list_for_each_entry(pos,head,member) \
        for(pos = list_first_entry(head,typeof(*pos),member)\
           &pos->member != (head) \
           pos = list_next_entry(pos,member))

pos 表示结构体变量,head 是头结点,member 是list_head 在结构体当中的名字,这个函数就是如果Pos 为非空,那么pos就是其本身,否则在链表头强制扩充一个虚指针,为了list_for_each_entry_continue()做准备

#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_next_entry(pos,member);           \
            &pos->member != (head);                   \
            pos = list_next_entry(pos,member))

同上从前边遍历

#define list_for_each_continue_reverse(pos,head,member)   \
        for(pos = list_prev_entry(pos,member);\
           &pos->member != (head);              \
           pos = list_prev_entry(pos,member))


#define list_safe_reset_next(pos,n,member)       \
        n = list_next_entry(pos,member)


内核哈希链表:

这个链表和我们平时常见的链表有很大的不同,哈希表的结构设计和普通的链表有很大的区别

首先哈希表的头结点和其它节点有很大的不同。

struct hlist_head{
    struct hlist_node *first;
};

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


这个链表首先使用的是hlist_head 节点做头结点,然后使用hlist_node 做其它节点,最后一个节点指向NULL
其中**prev 指向的是前一个节点的next.就是指向自己。


如果画成图就是这个样子的。

初始化以及判断是否为空

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

static inline void INIT_LIST_NODE(struct hlist_node *h)
{
    h->next = NULL;
    h->pprev = NULL;
}

static inline int hlist_unhashed(const struct hlist_node *n)
{
    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;  //首先删除N节点,申请一个next 指向n的下一个节点;
    struct hlist_node **prev = n->pprev;//申请一个二重指针开始指向n,
    *pprev = next;                      //n前面的next指向了n后面的元素
    if(next)                            //如果next非空,后一个元素赋值pprev
        next->pprev = pprev;
}


添加节点一共有三个函数
第一个向链表头结点后边增加节点
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,在next前边添加一个节点n
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;
}


//next must be != NULL ,在next后边添加一个节点n
static inline void hlist_add_behind(struct hlist_node *n,struct hlist_node *prev)
{
    n->next = prev->next;
    prev->next = n;
    n->pprev = &prev->next;
    if(n->next)
        n->next->pprev = &n->next;

}


下面是hlist的宏遍历
#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)


//这是一个hlist的宏的安全遍历,其中添加了变量n,防止在遍历过程中
//发生断链的情况

#define hlist_for_each_safe(pos,n.head) \
        for(pos = (head)->first;pos && ({n = pos->next;1;});\
           pos = n) \

可能有人对pos && ({n = pos->next;1;})

这一句也不理解,这个for循环还是在判断pos这个东西是不是为空值于后边的部分,其实就是一个赋值操作,但是它必须返回1,

所以就写成<pre name="code" class="cpp" style="font-size:18px;"> ({n = pos->next;1;})

这个样子了,这个部分始终返回1.

#define hlist_entry_safe(ptr,type,member) \
        ({typeof(ptr)___ptr = (ptr); \
          ___ptr?hlist_entry(___ptr,type,member):NULL; \
         })


遍历整个链表
#define hlist_for_each_entry(pos,head,member) \
        for(pos = hlist_entry_safe((head)->first),typeof(*(pos)),member); \
        pos;\
        pos = hlist_entry_safe((pos)->member.next,typeof(*(pos)),member)


#define hlist_for_each_entry_continue(pos, member)pos\
            for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
                         pos;for\
                         pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))


#define hlist_for_each_entry_from(pos, member)hlist_for_each_entry_from\
            for (; pos;typeof\
                         pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))


#define hlist_for_each_entry_safe(pos, n, head, member)         \
            for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
                         pos && ({ n = pos->member.next; 1;  });pos\
                         pos = hlist_entry_safe(n, typeof(*pos), member))


版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/zmrlinux/p/4921391.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值