zoukankan      html  css  js  c++  java
  • linux内核-双向链表

    linux中的经典宏定义

    offsetof

    定义:offsetof在linux内核的include/linux/stddef.h中定义。

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    说明:获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。

    (01) ( (TYPE *)0 ) 将零转型为TYPE类型指针,即TYPE类型的指针的地址是0。

    (02) ((TYPE *)0)->MEMBER 访问结构中的数据成员。

    (03) &( ( (TYPE *)0 )->MEMBER ) 取出数据成员的地址。由于TYPE的地址是0,这里获取到的地址就是相对MEMBER在TYPE中的偏移。

    (04) (size_t)(&(((TYPE*)0)->MEMBER)) 结果转换类型。对于32位系统而言,size_t是unsigned int类型;对于64位系统而言,size_t是unsigned long类型。

    container_of

    定义:container_of在linux内核的include/linux/kernel.h中定义。

    #define container_of(ptr, type, member) ({          
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    
        (type *)( (char *)__mptr - offsetof(type,member) );})

    说明:根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。

    (01) typeof( ( (type *)0)->member ) 取出member成员的变量类型。

    (02) const typeof( ((type *)0)->member ) *__mptr = (ptr) 定义变量__mptr指针,并将ptr赋值给__mptr。经过这一步,__mptr为member数据类型的常量指针,其指向ptr所指向的地址。

    (03) (char *)__mptr 将__mptr转换为字节型指针。

    (04) offsetof(type,member)) 就是获取"member成员"在"结构体type"中的位置偏移。

    (05) (char *)__mptr - offsetof(type,member)) 就是用来获取"结构体type"的指针的起始地址(为char *型指针)。

    (06) (type *)( (char *)__mptr - offsetof(type,member) ) 就是将"char *类型的结构体type的指针"转换为"type *类型的结构体type的指针"。

    Linux双向链表实现

    Linux双向链表的定义主要涉及到两个文件:

    include/linux/types.h

    include/linux/list.h

    (01). 节点定义

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

    虽然名称list_head,但是它既是双向链表的表头,也代表双向链表的节点。

    (02). 初始化节点

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

    #define LIST_HEAD(name)

                              struct list_head name = LIST_HEAD_INIT(name)

    static inline void INIT_LIST_HEAD(struct list_head *list)
    {
        list->next = list;
        list->prev = list;
    }

    LIST_HEAD的作用是定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。
    LIST_HEAD_INIT的作用是初始化节点:设置name节点的前继节点和后继节点都是指向name本身。
    INIT_LIST_HEAD和LIST_HEAD_INIT一样,是初始化节点:将list节点的前继节点和后继节点都是指向list本身。

    (03). 添加节点

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

    static inline void list_add(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);
    }

    __list_add(new, prev, next)的作用是添加节点:将new插入到prev和next之间。在linux中,以"__"开头的函数意味着是内核的内部接口,外部不应该调用该接口。
    list_add(new, head)的作用是添加new节点:将new添加到head之后,是new称为head的后继节点。
    list_add_tail(new, head)的作用是添加new节点:将new添加到head之前,即将new添加到双链表的末尾。

    (04). 删除节点

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

    static inline void __list_del_entry(struct list_head *entry)
    {
        __list_del(entry->prev, entry->next);
    }

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

    __list_del(prev, next) 和__list_del_entry(entry)都是linux内核的内部接口。
    __list_del(prev, next) 的作用是从双链表中删除prev和next之间的节点。
    __list_del_entry(entry) 的作用是从双链表中删除entry节点。

    list_del(entry) 和 list_del_init(entry)是linux内核的对外接口。
    list_del(entry) 的作用是从双链表中删除entry节点。
    list_del_init(entry) 的作用是从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。

    (05). 替换节点

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

    复制代码

    list_replace(old, new)的作用是用new节点替换old节点。

    (06). 判断双链表是否为空

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

    list_empty(head)的作用是判断双链表是否为空。它是通过区分"表头的后继节点"是不是"表头本身"来进行判断的。

    (07). 获取节点

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

    list_entry(ptr, type, member) 实际上是调用的container_of宏。
    它的作用是:根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。

    (08). 遍历节点

    #define list_for_each(pos, head) 
        for (pos = (head)->next; pos != (head); pos = pos->next)
    
    #define list_for_each_safe(pos, n, head) 
        for (pos = (head)->next, n = pos->next; pos != (head); 
            pos = n, n = pos->next)

    list_for_each(pos, head)和list_for_each_safe(pos, n, head)的作用都是遍历链表。但是它们的用途不一样!
    list_for_each(pos, head)通常用于获取节点,而不能用到删除节点的场景。
    list_for_each_safe(pos, n, head)通常删除节点的场景。

  • 相关阅读:
    洛谷p1017 进制转换(2000noip提高组)
    Personal Training of RDC
    XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Eurasia
    XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Peterhof.
    Asia Hong Kong Regional Contest 2019
    XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Siberia
    XVIII Open Cup named after E.V. Pankratiev. Ukrainian Grand Prix.
    XVIII Open Cup named after E.V. Pankratiev. GP of SPb
    卜题仓库
    2014 ACM-ICPC Vietnam National First Round
  • 原文地址:https://www.cnblogs.com/lucas-hsueh/p/3734090.html
Copyright © 2011-2022 走看看