设为首页 收藏本站
查看: 1095|回复: 6

[经验分享] linux的数据结构---链表

[复制链接]

尚未签到

发表于 2012-9-4 09:04:06 | 显示全部楼层 |阅读模式
链表是一种常见的组织有序数据的数据结构,相对于数组,具有更好的动态性,可以高效的在链表的任意位置实时的插入或者删去。在linux的源代码中,大量的使用了链表。通常链表数据结构至少有两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。在数据结构中定义一个指向任意类型为



[Shell] 纯文本查看 复制代码
struct list

{

    void *data;

    struct list *next;

};


而在内核中,定义了一种通用的双向循环链表,链表的定义为:




[Shell] 纯文本查看 复制代码
struct list_head {

    struct list_head *next, *prev;

};


有prev和next两个指针,分别指向链表中的前一节点和后一节点。在初始化的时候,都指向自己本身。



    [Shell] 纯文本查看 复制代码
    
    static inline void INIT_LIST_HEAD(struct list_head *list)
    
    {
    
        list->next = list;
    
        list->prev = list;
    
    }


向链表中添加节点




[Shell] 纯文本查看 复制代码
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和list_add_tail的区别不大,linux实现两个接口,在表头插入是插入在head后,而在表尾插入是在head->prev之后,只是表明插入到该节点之后或者之前。链表中的节点删去




[Shell] 纯文本查看 复制代码
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 = (void *)0xDEADBEEF;

    entry->prev =(void *)0xBEEFDEAD;

}


被删去entry删去后分别指向两个值,这个设置是为了保持不在链表中的节点项以后不会被访问。




[Shell] 纯文本查看 复制代码
static inline void list_del_init(struct list_head *entry)

{

    __list_del_entry(entry);

    INIT_LIST_HEAD(entry);

}


该函数将节点从链表中解下来后,调用INIT_LIST_HEAD将节点设置为空链表。




[Shell] 纯文本查看 复制代码
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);

}


list_replace是将链表中的一个老节点换乘一个新节点,从实现上来看,即使old所在链表只有一个old节点,new也可以成功替换。
[Shell] 纯文本查看 复制代码
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);

}



list_move的作用是把list节点从原链表中去除,并加入到新的链表中;而list_move_tail只是加入新链表。前者是加到链表的头部,而后者是加入到链表的尾部。




[Shell] 纯文本查看 复制代码
static inline int list_is_last(const struct list_head *list,

const struct list_head *head)

{

    return list->next == head;

}


上面函数主要是判断list是否处于head链表的尾部。




[Shell] 纯文本查看 复制代码
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);

}


list_empty判断head链表是否为空,而list_empty_careful同样也是判断链表是否为空,只是多了一个检测条件。


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


list_is_singular判断head是否只有一个节点,在检测链表头head外是否只有一个节点。list的操作包括链表的节点添加和删去,节点从一个链表转移到另外一个链表,替换和合并,由于其数据结构中只是定义了两个指针域,没有定义数据域。那么怎么去获得数据域呢?linux中有一种新的方法来获取,通过获取一个结构体中一个成员在这个结构体中的偏移。

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


来个例子,例如


  • typedef struct
  • {
  •     int i;
  •     int j;
  • }ember;


这个ember结构体占用了8个字节,假设声明一个变量ember a;想知道a的地址该如果办呢?只要知道j在a中的偏移量,然后把j的地址减去这个偏移量就是a的地址。list_entry主要用于从list节点查找其内嵌的结构。


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


list_first_entry是将ptr看成一个链表头,取出其中第一个节点对应的结构地址。下面来看看在总线设备驱动模型中大量被使用的list_for_each,循环遍历链表中的每个节点,从链表的头部的第一个节点,一直到链表的尾部。


  • #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)


list_for_each_prev从链表的尾部逆向遍历到链表头。


  • #define list_for_each_entry(pos, head, member)                \
  •     for (pos = list_entry((head)->next, typeof(*pos), member);    \
  •      &pos->member != (head);     \
  •      pos = list_entry(pos->member.next, typeof(*pos), member))


上面这个函数不是遍历链表的节点,而是遍历链表节点锁嵌套的结构。其等价为list_for_each加list_entry。在list_head,有以上一些通用的链表的形式,但是还有一种klist和hlist也占据了很大的一部分,hlist也就是哈希表。哈希表也是一个哈希数组,为了解决映射冲突的问题,其实哈希组的每一个项做成一个链表,哈希组的项很多,list_head的话每个链表都需要两个指针空间,就发明了hlist,一是它的链表只需要一个指针,二是它的每一项都可以找到前一个节点。
[Shell] 纯文本查看 复制代码

struct klist_node;

struct klist {

    spinlock_t        k_lock;

    struct list_head    k_list;

    void            (*get)(struct klist_node *);

    void            (*put)(struct klist_node *);

} __attribute__ ((aligned (sizeof(void *))));



#define KLIST_INIT(_name, _get, _put)\

{ .k_lock    = __SPIN_LOCK_UNLOCKED(_name.k_lock),\

.k_list    = LIST_HEAD_INIT(_name.k_list),\

.get= _get,\

.put        = _put, }



#define DEFINE_KLIST(_name, _get, _put)\

    struct klist _name = KLIST_INIT(_name, _get, _put)



extern void klist_init(struct klist *k, void (*get)(struct klist_node *),

         void (*put)(struct klist_node *));



struct klist_node {

    void            *n_klist;/* never access directly */

    struct list_head    n_node;

    struct kref        n_ref;

};



可以看到,klist的链表头是一个struct klist结构,链表节点是struct klist_node结构。先看看struct klist,包含链表需要的k_list,用于加锁的k_lock,get()和put()用于对结构的引用。这样在节点的初始化调用get(),在节点删去时调用put()。在看看struct klist_node,除了链表需要的node,一个引用计数,还有一个n_klist指针。初始化klist




[Shell] 纯文本查看 复制代码
void klist_init(struct klist *k, void (*get)(struct klist_node *),

        void (*put)(struct klist_node *))

{

    INIT_LIST_HEAD(&k->k_list);

    spin_lock_init(&k->k_lock);

    k->get = get;

    k->put = put;

}




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-1031-1-1.html 上篇帖子: 内核内存池--mempool 下篇帖子: 修改linux系统时间 linux 结构

尚未签到

发表于 2013-3-18 20:15:40 | 显示全部楼层
解释就是掩饰,掩饰就是编故事!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-5-18 01:34:55 | 显示全部楼层
为中华而努力读书!一包中华好多钱啊~~~

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-5-29 07:42:57 | 显示全部楼层
男人偷腥时的智商仅次于爱因斯坦!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-6-15 04:46:34 | 显示全部楼层
花前月下,不如花钱“日”下!*^_^*

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-6-23 05:20:38 | 显示全部楼层
不在课堂上沉睡,就在酒桌上埋醉。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-7-1 04:07:07 | 显示全部楼层
微机原理闹危机,随机过程随机过,实变函数学十遍,汇编语言不会编!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表