Linux XArray详解

2023年 7月 13日 87.9k 0

关注微信公众号:Linux内核拾遗

文章来源:mp.weixin.qq.com/s/UnoxxfpU3…

1 引言

在现代操作系统中,高效的数据结构对于处理大规模数据和高并发访问非常重要。Linux内核作为一个开放源代码的操作系统内核,一直致力于改进数据结构以提高性能和可扩展性。其中一个引人注目的数据结构是Linux XArray(扩展数组)。

Linux XArray是一种高效的键值对数据结构,旨在解决大规模数据集上的高并发访问问题。它被广泛应用于Linux内核的各个子系统,如文件系统、网络子系统和内存管理等。

本文将深入探讨XArray的设计和实现细节,重点介绍XArray的数据结构、基本操作和扩展功能。通过本文的介绍,读者将能够更好地理解Linux XArray的作用和优势。

2 XArray基本概念

2.1 XArray的定义和特点

XArray是一种高效的键值对数据结构,用于在Linux内核中管理大规模数据集。XArray的核心思想是通过索引来跟踪和定位数据项,而不是依赖于哈希函数或平衡树结构。它使用一组指针和键来组织数据,通过这些指针和键,可以快速地定位到数据项,而无需遍历整个数据集。XArray通过采用特殊的索引和跳表结构,提供了快速的插入、删除、更新和查询操作。

XArray的索引结构由一系列的节点组成,每个节点包含一个键和指向下一个节点的指针。节点之间通过指针相互链接,形成一个跳表结构。这种结构允许在插入、删除和查询操作中快速地定位到目标数据项,同时保持较低的时间复杂度。

Linux XArray是一种扩展数组(eXtensible Array)数据结构,它定义和实现在Linux内核中,提供了一种通用的数据管理机制,可以被不同的子系统使用。

XArray具备以下特点:

  • 高性能:XArray经过精心设计和优化,具有高度优化的算法和数据结构,以实现在大规模数据集上的快速操作。它可以在O(log N)的时间复杂度内执行数据的查找和修改操作,具备出色的性能。
  • 低内存占用:XArray在内存使用方面非常高效。它能够动态调整内存的分配和回收,根据需要扩展或收缩存储空间。这使得XArray在管理大规模数据集时能够有效地节省内存,减少资源消耗。
  • 可扩展性:XArray的设计允许动态地扩展和收缩,以适应不同规模和负载的系统需求。它可以根据数据集的大小自动调整内部结构,并保持高性能和高效率。这使得XArray适用于各种应用场景和系统需求。
  • 线程安全:XArray在设计上考虑了并发访问的情况,并提供了相应的线程安全机制。它使用自旋锁等同步机制来保护数据的一致性和正确性,以确保在多线程环境下的安全访问。
  • 应用广泛:XArray被广泛应用于Linux内核的各个子系统,如文件系统、网络子系统和内存管理等。它在这些子系统中提供了高性能和可靠的数据管理能力,为系统的性能和效率提供了重要的支持。
  • 2.2 XArray与传统数据结构的比较

  • 数组:相比于数组,XArray的大小可以动态增长或收缩,避免了数组长度固定的限制。
  • 链表:与链表相比,XArray通过索引和跳表的结构,提供了更快的随机访问和范围查询能力。
  • 哈希表:与哈希表相比,XArray不需要计算哈希值和处理哈希冲突,简化了操作逻辑,同时在大规模数据集上具有更好的性能。
  • 红黑树:与红黑树相比,XArray在插入和删除操作上更具优势,且具备更低的内存占用。
  • 2.3 XArray的应用场景

  • 文件系统:XArray可用于管理文件系统中的索引节点(Inode)和文件描述符等数据结构。
  • 网络子系统:XArray可用于管理网络连接和套接字相关的数据结构,提供高性能的连接管理能力。
  • 内存管理:XArray可用于管理内核中的页表、内存分配器和缓存等数据结构,优化内存管理的性能和效率。
  • 虚拟化:XArray可用于虚拟化平台中的资源管理,如虚拟机的状态跟踪和调度等。
  • 3 XArray的设计与实现

    3.1 XArray的数据结构

    XArray的数据结构在Linux内核源代码中的实现位于头文件include/linux/xarray.h中:

    struct xa_node:这是XArray的基本节点结构,表示XArray中的节点,用于存储键值条目(entry)。

    struct xa_node {
    	unsigned char	shift;		/* Bits remaining in each slot */
    	unsigned char	offset;		/* Slot offset in parent */
    	unsigned char	count;		/* Total entry count */
    	unsigned char	nr_values;	/* Value entry count */
    	struct xa_node __rcu *parent;	/* NULL at top of tree */
    	struct xarray	*array;		/* The array we belong to */
    	union {
    		struct list_head private_list;	/* For tree user */
    		struct rcu_head	rcu_head;	/* Used when freeing node */
    	};
    	void __rcu	*slots[XA_CHUNK_SIZE];
    	union {
    		unsigned long	tags[XA_MAX_MARKS][XA_MARK_LONGS];
    		unsigned long	marks[XA_MAX_MARKS][XA_MARK_LONGS];
    	};
    };
    
    • shiftshift是一个无符号字符(unsigned char),用于表示每个槽(slot)中剩余的位数。它确定了键(index)的位置,即用于计算在当前节点的槽位中的偏移量。
    • offsetoffset是一个无符号字符(unsigned char),用于表示当前节点在父节点中的槽位偏移量。通过偏移量,可以在父节点的槽位中定位到当前节点。
    • countcount是一个无符号字符(unsigned char),用于表示当前节点中的总条目数。它统计了当前节点中存储的所有条目的数量。
    • nr_valuesnr_values是一个无符号字符(unsigned char),用于表示当前节点中的值条目数。它记录了当前节点中存储的值类型的条目的数量。
    • parentparent是指向父节点的指针。它允许在XArray的层次结构中进行导航,从而实现了对上层节点的访问。
    • arrayarray是指向所属XArray的指针。它指示当前节点所属的XArray对象,用于在操作中获取相关的XArray属性和功能。
    • private_listprivate_list是用于树用户的私有链表。它在树结构中的用户定义中使用,以实现自定义的树结构功能。
    • rcu_headrcu_head是用于在释放节点时使用的RCU(Read-Copy-Update)机制的头结构。RCU是一种用于实现高效内存回收的机制。
    • slots[XA_CHUNK_SIZE]slots是一个指针数组,用于存储指向子节点或条目的指针。它具体指向当前节点的子节点或存储在当前节点中的条目。
    • tags[XA_MAX_MARKS][XA_MARK_LONGS]tags是一个位图数组,用于存储键的标记位图。它支持多个标记,每个标记由一个位图表示。
    • marks[XA_MAX_MARKS][XA_MARK_LONGS]marks是一个位图数组,用于存储节点的标记位图。它支持多个标记,每个标记由一个位图表示。

    struct xarray: 这是XArray的主要结构,用于管理XArray的属性和根节点。

    struct xarray {
    	spinlock_t	xa_lock;
    	/* private: The rest of the data structure is not to be used directly. */
    	gfp_t		xa_flags;
    	void __rcu *	xa_head;
    };
    
    • spinlock_t xa_lock: 这是一个自旋锁(spinlock),用于保护XArray的并发访问。自旋锁是一种用于同步访问共享资源的锁,它使用忙等待的方式来阻塞其他线程或进程的访问,直到锁可用。在访问XArray时,需要先获取该锁来确保数据的一致性和正确性。
    • gfp_t xa_flags: 这是一个标志位,用于指定内存分配的行为和特性。gfp_t是一种用于描述内存分配的标志类型,在Linux内核中广泛使用。通过设置不同的标志位,可以指定内存分配的方式、要求和约束,以满足特定的内存管理需求。
    • void __rcu * xa_head: 这是一个指针,指向XArray的头部。__rcu是一种用于实现内核中的读-复制-更新(RCU)机制的修饰符。RCU是一种无锁机制,用于在多线程环境中实现高效的并发访问。通过使用RCU修饰符,可以确保在读取XArray时不会阻塞其他线程对XArray的修改操作,从而提高并发性能。

    XArray的其他辅助数据结构和函数在源代码中也有定义和实现,用于支持XArray的操作和功能,如用于标记槽的状态的位图(bitmap)、迭代器等。

    3.2 XArray的基本操作

    XArray的基本操作在Linux内核源代码中的实现涉及一系列函数和宏定义。以下是对XArray的基本操作的简要介绍。

    3.2.1 插入数据

    插入数据是向XArray中添加新的键值对的操作。

    • 函数:void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);

    • 作用:向XArray中插入一个键值对。

    • 源代码位置:lib/xarray.c

    • 示例用法:

      xa_store(&xarray, index, value, GFP_KERNEL);
      

    函数的步骤如下:

  • 初始化XArray的状态和位置。
  • 检查参数的有效性,确保键值对数据不是一个特殊的高级指针,并根据需要设置空值。
  • 进入一个循环,尝试将键值对数据存储到XArray中。如果XArray中的位置已经被占用,就尝试将数据存储到下一个位置,直到成功存储或者发现内存不足。
  • 返回存储结果,表示存储操作是否成功。
  • 3.2.2 删除数据

    删除数据是从XArray中移除指定键值对的操作。

    • 函数:void *xa_erase(struct xarray *xa, unsigned long index);

    • 作用:从XArray中删除指定键的条目。

    • 源代码位置:lib/xarray.c

    • 示例用法:

      xa_erase(&xarray, index);
      

    函数的步骤如下:

    • 准备工作:函数接收一个指向XArray的指针和一个索引作为参数。然后创建一个XArray状态(XA_STATE)对象,并将其与XArray和索引关联起来。
    • 删除操作:使用XA_STATE对象,函数尝试在XArray中查找指定索引的键值对。如果找到了对应的键值对,就将其删除,并将结果存储在XArray状态对象中。
    • 返回结果:函数通过调用xas_store()xas_result()函数将删除操作的结果返回。

    3.2.3 更新数据

    更新数据是修改XArray中指定键对应的值的操作。

    • 函数:void *xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)

    • 作用:更新XArray中指定键的值。

    • 源代码位置:lib/xarray.c

    • 示例用法:

      xa_store(&xarray, index, new_value, GFP_KERNEL);
      

    函数的步骤同插入数据。

    3.2.4 查询数据

    查询数据是根据给定键从XArray中检索相应的值的操作。

    • 宏:void *xa_load(struct xarray *xa, unsigned long index);

    • 作用:从XArray中获取指定键的值。

    • 源代码位置:include/linux/xarray.h

    • 示例用法:

      value = xa_load(&xarray, index);
      

    函数的步骤如下:

  • 创建XArray状态变量: 使用XA_STATE宏,创建一个名为xasXArray状态变量,该变量用于在XArray中定位和访问指定索引处的条目。
  • 获取读取锁: 使用rcu_read_lock函数,获取RCU读取锁。这是一种读取操作所需的锁,它允许多个读取操作同时进行,而不会阻塞其他读取操作。
  • 循环加载条目: 进入一个循环,通过调用xas_load函数从XArray中加载指定索引处的条目,并将结果存储在entry变量中。如果加载的条目是空条目(即没有有效数据),则将entry置为NULL
  • 处理加载重试: 使用xas_retry函数,检查在加载条目期间是否需要进行重试。如果需要重试,函数会重新加载条目,并继续循环,直到成功加载非空条目或不再需要重试。
  • 释放读取锁: 使用rcu_read_unlock函数,释放之前获取的RCU读取锁。
  • 返回加载的条目: 返回最终加载的条目(可能为NULL),作为函数的结果。
  • 3.3 XArray的扩展功能

    3.3.1 范围查询

    XArray支持范围查询,允许按照键的范围进行查询操作。范围查询可以通过遍历槽中的链表和跳表结构来实现。通过设置起始键和结束键,可以检索指定范围内的键值对。

    范围查询功能的实现主要涉及使用xa_find_range()函数进行遍历和检索。

    int xa_find_range(struct xarray *xa, unsigned long start, unsigned long end,
                      xa_tag_t tag, void *result[]);
    

    xa_find_range()函数接受一个起始键和结束键,以及一个用于存储结果的数组result[]。它将在指定范围内搜索符合条件的键值对,并将结果存储在result[]中。

    以下是一个示例代码片段,演示如何使用范围查询功能:

    unsigned long start_key = 100;
    unsigned long end_key = 200;
    void *result[XA_CHUNK_SIZE];
    
    int num_entries = xa_find_range(&xarray, start_key, end_key, XA_PRESENT, result);
    if (num_entries > 0) {
        // 处理查询结果
    }
    

    3.3.2 迭代器

    XArray提供迭代器功能,使得可以逐个遍历XArray中的键值对。迭代器可以按照特定的顺序遍历槽中的链表和跳表结构,以获取所有的键值对。

    迭代器可以使用xa_for_each()xa_for_each_marked()函数来实现。

    #define XA_FLAGS_LOCK_IRQ	0x1
    
    int xa_for_each(struct xarray *xa, unsigned long *index, void *entry, xa_mark_t mark);
    int xa_for_each_marked(struct xarray *xa, unsigned long *index, void *entry,
                           xa_mark_t mark, xa_mark_t *last);
    

    xa_for_each()函数按顺序迭代XArray中的每个条目,而xa_for_each_marked()函数只迭代标记为特定标记的条目。

    以下是一个示例代码片段,演示如何使用迭代器功能:

    unsigned long index = 0;
    void *entry;
    
    xa_for_each(&xarray, &index, entry, XA_PRESENT) {
        // 处理迭代到的键值对
    }
    

    4 综合示例

    以下是一个简单的代码示例,演示如何使用Linux XArray进行插入、删除和查询操作:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    MODULE_LICENSE("GPL");
    
    struct my_data {
        int id;
        char name[20];
    };
    
    DEFINE_XARRAY(my_xarray);  // 定义一个全局的XArray
    
    static void insert_data(int id, const char* name) {
        struct my_data* data = kmalloc(sizeof(struct my_data), GFP_KERNEL);
        data->id = id;
        strncpy(data->name, name, sizeof(data->name));
        
        xa_store(&my_xarray, id, data, GFP_KERNEL);
    }
    
    static void remove_data(int id) {
        struct my_data* data = xa_erase(&my_xarray, id);
        if (data)
            kfree(data);
    }
    
    static struct my_data* get_data(int id) {
        return xa_load(&my_xarray, id);
    }
    
    static int __init xarray_example_init(void) {
        pr_info("xarray_example: Initializing XArray example modulen");
    
        insert_data(1, "John");
        insert_data(2, "Alice");
        insert_data(3, "Bob");
        insert_data(4, "Steve");
    
        struct my_data* data = get_data(2);
        if (data)
            pr_info("xarray_example: ID: %d, Name: %sn", data->id, data->name);
    
        unsigned long index;
    
        pr_info("xarray_example: before remove data 3");
        xa_for_each(&my_xarray, index, data) {
            pr_info("index = %d, data = %p, data->id = %d, data->name = %sn", 
                    index, data, data->id, data->name);
        }
    
        remove_data(3);
    
        pr_info("xarray_example: after remove data 3n");
        xa_for_each(&my_xarray, index, data) {
            pr_info("index = %d, data = %p, data->id = %d, data->name = %sn", 
                    index, data, data->id, data->name);
        }
    
        return 0;
    }
    
    static void __exit xarray_example_exit(void) {
        pr_info("xarray_example: Cleaning up XArray example modulen");
    
        struct my_data* data;
        unsigned long index;
    
        xa_for_each(&my_xarray, index, data) {
            xa_erase(&my_xarray, index);
            kfree(data);
        }
    }
    
    module_init(xarray_example_init);
    module_exit(xarray_example_exit);
    

    在上面的示例中,我们使用了DEFINE_XARRAY宏来定义一个全局的XArray,命名为my_xarray。然后,我们定义了insert_data函数来插入数据,remove_data函数来删除数据,以及get_data函数来查询数据。在test_xarray函数中,我们进行了一些简单的插入、查询和删除操作,并最后调用cleanup_xarray函数来清理XArray中的所有数据。

    上述示例的输出结果如下:

    image-20230707123132011

    5 总结

    Linux XArray是一种高效的键值对数据结构,在Linux内核中用于管理大规模数据集。它通过索引和跳表的结构,实现了快速的插入、删除、更新和查询操作。XArray具有低内存占用、可扩展性和线程安全性等特点,使其在文件系统、网络子系统、内存管理和虚拟化等领域具有广泛的应用潜力。通过XArray,Linux内核能够以高性能和高效率处理复杂的数据操作需求,为系统的性能和可扩展性提供了重要的支持。

    6 参考资料

  • XArray:www.kernel.org/doc/html/v5…。
  • 关注微信公众号:Linux内核拾遗

    文章来源:mp.weixin.qq.com/s/UnoxxfpU3…

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论