• Linux 中的链表list 和 hlist


    前言

    在网上发现关于此的文章转载到处都是。我也就不再做重复性的工作了,将我觉得重要的做下笔记就好了,整理下思路。

    概况

    链表是经典的组织有序数据的数据结构,是线性表的一种重要实现方式(另外一种是数组)。其基本思想是通过指针将一系列数据节点连接成一条数据链,达到有序组织数据的目的。相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。

    通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。按照指针域的组织以及各个节点之间的联系形式,链表有许多变种,如单链表,双链表,带头(尾)节点的链表,循环链表等。其中在参考文献中讨论相关各个结构的效率问题。

    View Code
    数组的特点

    1 .固定空间,并且空间连续
    2 .连续存储,支持随机访问
    3 .数据局部性好
    4 .插入删除节点开销比较大,要移动元素以维持随机访问的特性。

    所以,对于经常访问元素时,我们采用数组的方式来实现;如果是经常插入和删除元素时,我们采用链表来实现。

    单链表只能单向遍历,而双链表可以在链表的任意节点进行遍历全链表,但是也带来了内存的开销(小数据无所谓,当数据量大到一定程度,开销还是很可观的)。

    图示中演示的是带头节点的链表,即浪费一个节点,换取插入与删除的一致性(不必判断链表插入时或删除后是否为空的情况)。

                                                     单链表示意图

                                      双链表示意图

    Linux下的实现

    Linux 内核中使用了大量的链表结构来组织数据,包括设备列表以及各种功能模块中的数据组织。这些链表大多采用在 [include/linux/list.h] 实现的一个相当精彩的链表数据结构。

    数据结构书上演示的程序,一般是节点中包含数据,定义如下

     

    typedef int data_t ;
    struct
    node
    {
    struct node *next;
    struct node *priv;
    data_t data;//真实数据
    }

    但是在Linux 的实现中,节点与数据是分离的。 如果一个结构需要用到链表结构,直接包含链表hlist_head或list_head即可。如此在链表中链表头只需用一个struct list_head类型来表示即可,不管链表中节点的数据结构多么庞大,链表头只需要占4个字节或者8个字节。如下是Linux hlist list的定义。

    /* *单链表 */
    struct hlist_head {
    struct hlist_node * first;
    };
    struct hlist_node {
    struct hlist_node * next, ** pprev;
    };


    /* *双链表 */
    struct list_head {
    struct list_head * next, ** prev;
    };

    对于定义,我们或许更加习惯分开成为如下格式

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

    注:hlist是单链表的实现 (h 代表 hash ,用在 hash ) list 是双链表的实现( task 等都有赖于该实现)

    请注意hlist 的实现, prev 用到了二级指针,这种差异带来了相关插入删除等操作的不同(在后文分析)。图示给出了多个节点链接的情况(很多图示没有第一个节点的 pprev 域指向 first 的指针,请注意)。

    Linux 内核链表中,需要用链表组织起来的数据通常会包含一个struct list_head成员。

    如参考文献1 中就给出了nf_sockopt_ops结构的示意图

    struct nf_sockopt_ops的定义为

    struct nf_sockopt_ops
    {
    struct list_head list;
    u_int8_t pf;
    ……
    int ( * set )( struct sock * sk, int optval, void __user * user, unsigned int len);
    ……
    int ( * compat_get)( struct sock * sk, int optval,
    void __user * user, int * len);
    ……
    struct module * owner;
    };

    在nf_sockopt_ops开始的部分就有一个struct list_head list,注意不是指针(占用8 字节 @32 位机器上)。

    对于RCU 的讨论请参考 RCU 同步机制》

    参考文献
  • 相关阅读:
    洞察僵尸网络的4条关键线索,你知道吗?
    数据即服务(DaaS)的好处和趋势
    AIOT:什么是智联网,它是未来吗?
    渐变略过效果
    页面头部banner动画效果
    小三角
    监测屏幕宽度
    开关效果
    高级轮播
    手机端跳转页面指定楼层
  • 原文地址:https://www.cnblogs.com/westfly/p/1977201.html
Copyright © 2020-2023  润新知