浅析Open vSwitch数据结构:哈希表hmap/smap/shash

news2025/1/12 18:53:39

文章目录

    • 概述
    • hmap
      • hmap数据结构
      • 初始化hmap
      • 插入节点
      • 扩展hmap空间
        • resize函数
      • 删除节点
      • 遍历所有节点
        • 辅助函数hmap_first
        • 辅助函数hmap_next
    • smap
      • smap数据结构
      • 插入节点
      • 删除节点
      • 查找节点
      • 遍历所有节点
    • shash
      • shash数据结构
      • 插入节点
      • 删除节点
      • 查找节点
      • 遍历所有节点

概述

在OVS软件中,hmap提供了基础的哈希表存储结构,smap和shash基于hmap进行实现,其中smap支持存储字符串,而sshash则支持存储任意类型的数据。

hmap

OVS软件的hmap是基于分离链接法实现的,分离链接法使用链表解决散列冲突,其做法是散列值相同的元素都保存到一个链表中。当查询的时候,首先根据散列值找到元素所在的链表,然后遍历链表查找对应的元素。
在这里插入图片描述

hmap数据结构

struct hmap {
    struct hmap_node **buckets;     // 哈希数组,本质上是一个分离链表数组,当mask=0时,指向成员one的地址
    struct hmap_node *one;      // 仅在mask为0时使用
    size_t mask;    // 哈希桶的大小
    size_t n;   // 哈希表中存储的hmap_node节点数量
};

// 哈希节点
struct hmap_node {
    size_t hash;    // 哈希值
    struct hmap_node *next;    // 单向链表,所有哈希值相同的节点会链接到一个链表中
};

初始化hmap

void hmap_init(struct hmap *hmap)
{
    hmap->buckets = &hmap->one;
    hmap->one = NULL;
    hmap->mask = 0;
    hmap->n = 0;
}

插入节点

#define hmap_insert(HMAP, NODE, HASH) \
    hmap_insert_at(HMAP, NODE, HASH, OVS_SOURCE_LOCATOR)
    
static inline void
hmap_insert_at(struct hmap *hmap, struct hmap_node *node, size_t hash,
               const char *where)
{
    hmap_insert_fast(hmap, node, hash);     // 执行快速插入
    if (hmap->n / 2 > hmap->mask) {     // 当存储节点数量的一半大于mask时,需要对hmap进行扩容
        hmap_expand_at(hmap, where);
    }
}

static inline void
hmap_insert_fast(struct hmap *hmap, struct hmap_node *node, size_t hash)
{
    struct hmap_node **bucket = &hmap->buckets[hash & hmap->mask];      // 计算hash值,并根据hash值确定数组索引
    node->hash = hash;
    node->next = *bucket;
    *bucket = node;     // 新节点插入到链表头部
    hmap->n++;      // 更新hmap存储节点数量
}

扩展hmap空间

void
hmap_expand_at(struct hmap *hmap, const char *where)
{
    size_t new_mask = calc_mask(hmap->n);       // 根据已存储节点的数量计算出新的mask值
    if (new_mask > hmap->mask) {
        COVERAGE_INC(hmap_expand);
        resize(hmap, new_mask, where);
    }
}

static size_t
calc_mask(size_t capacity)
{
    size_t mask = capacity / 2;
    mask |= mask >> 1;
    mask |= mask >> 2;
    mask |= mask >> 4;
    mask |= mask >> 8;
    mask |= mask >> 16;
#if SIZE_MAX > UINT32_MAX
    mask |= mask >> 32;
#endif

    mask |= (mask & 1) << 1;

    return mask;
}

resize函数

static void
resize(struct hmap *hmap, size_t new_mask, const char *where)
{
    struct hmap tmp;
    size_t i;

    ovs_assert(is_pow2(new_mask + 1));

    hmap_init(&tmp);        // 初始化临时的hmap
    if (new_mask) {
        tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1));    // 根据new_mask创建哈希数组
        tmp.mask = new_mask;
        for (i = 0; i <= tmp.mask; i++) {
            tmp.buckets[i] = NULL;
        }
    }
    int n_big_buckets = 0;
    int biggest_count = 0;
    int n_biggest_buckets = 0;
    for (i = 0; i <= hmap->mask; i++) {
        struct hmap_node *node, *next;
        int count = 0;
        for (node = hmap->buckets[i]; node; node = next) {
            next = node->next;
            hmap_insert_fast(&tmp, node, node->hash);       // 将原hmap的元素插入到临时hmap中
            count++;
        }
    }
    hmap_swap(hmap, &tmp);  // 交换两个hmap的结构,此时hmap的数据与原tmp是相同的
    hmap_destroy(&tmp);
}

删除节点

static inline void
hmap_remove(struct hmap *hmap, struct hmap_node *node)
{
    struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask];
    while (*bucket != node) {
        bucket = &(*bucket)->next;
    }
    *bucket = node->next;
    hmap->n--;
}

遍历所有节点

// 普通版本的遍历,不支持删除节点
#define HMAP_FOR_EACH(NODE, MEMBER, HMAP) \
    HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, (void) 0)

#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...)                     \
    for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \
         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \
         || ((NODE = NULL), false);                                     \
         ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER))

// 安全版本的遍历,支持删除节点
#define HMAP_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HMAP) \
    HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, (void) 0)

#define HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, ...)          \
    for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \
         ((NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))               \
          || ((NODE = NULL), false)                                     \
          ? INIT_CONTAINER(NEXT, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), 1 \
          : 0);                                                         \
         (NODE) = (NEXT))

辅助函数hmap_first

static inline struct hmap_node *
hmap_next__(const struct hmap *hmap, size_t start)
{
    size_t i;
    for (i = start; i <= hmap->mask; i++) {
        struct hmap_node *node = hmap->buckets[i];
        if (node) {
            return node;
        }
    }
    return NULL;
}

static inline struct hmap_node *
hmap_first(const struct hmap *hmap)
{
    return hmap_next__(hmap, 0);
}

辅助函数hmap_next

static inline struct hmap_node *
hmap_next(const struct hmap *hmap, const struct hmap_node *node)
{
    return (node->next
            ? node->next        // 下一节点非空,返回下一节点
            : hmap_next__(hmap, (node->hash & hmap->mask) + 1));    
}

smap

smap基于hmap实现,是一个专用于存储key-value的哈希表,其中key和value类型都是字符串。

smap数据结构

struct smap {
    struct hmap map; 
};

struct smap_node {
    struct hmap_node node;  
    char *key;
    char *value;
};

插入节点

struct smap_node *
smap_add(struct smap *smap, const char *key, const char *value)
{
    size_t key_len = strlen(key);
    return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
                      hash_bytes(key, key_len, 0));     // 为key和value申请内存空间
}

static struct smap_node *
smap_add__(struct smap *smap, char *key, void *value, size_t hash)
{
    struct smap_node *node = xmalloc(sizeof *node);
    node->key = key;
    node->value = value;
    hmap_insert(&smap->map, &node->node, hash);     // 调用hmap接口插入节点
    return node;
}

删除节点

void
smap_remove_node(struct smap *smap, struct smap_node *node)
{
    hmap_remove(&smap->map, &node->node);
    free(node->key);
    free(node->value);
    free(node);
}

void
smap_remove(struct smap *smap, const char *key)
{
    struct smap_node *node = smap_get_node(smap, key);

    if (node) {
        smap_remove_node(smap, node);
    }
}

查找节点

struct smap_node *
smap_get_node(const struct smap *smap, const char *key)
{
    size_t key_len = strlen(key);
    return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
}

static struct smap_node *
smap_find__(const struct smap *smap, const char *key, size_t key_len,
            size_t hash)
{
    struct smap_node *node;

    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
        if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
            return node;
        }
    }

    return NULL;
}

遍历所有节点

// 普通版本的遍历,不支持删除,调用hmap的接口
#define SMAP_FOR_EACH(SMAP_NODE, SMAP)                                  \
    HMAP_FOR_EACH_INIT (SMAP_NODE, node, &(SMAP)->map,                  \
                        BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *), \
                        BUILD_ASSERT_TYPE(SMAP, struct smap *))

// 安全版本的遍历,支持删除,调用hmap的接口
#define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP)           \
    HMAP_FOR_EACH_SAFE_INIT (                               \
        SMAP_NODE, NEXT, node, &(SMAP)->map,                \
        BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *),   \
        BUILD_ASSERT_TYPE(NEXT, struct smap_node *),        \
        BUILD_ASSERT_TYPE(SMAP, struct smap *))

shash

shash也是基于hmap进行扩展,支持存储任意类型的数据。

shash数据结构

struct shash_node {
    struct hmap_node node;
    char *name;
    void *data;
};

struct shash {
    struct hmap map;
};

插入节点

static struct shash_node *
shash_add_nocopy__(struct shash *sh, char *name, const void *data, size_t hash)
{
    struct shash_node *node = xmalloc(sizeof *node);
    node->name = name;
    node->data = CONST_CAST(void *, data);      // 去除const属性进行存储
    hmap_insert(&sh->map, &node->node, hash);
    return node;
}

struct shash_node *
shash_add_nocopy(struct shash *sh, char *name, const void *data)
{
    return shash_add_nocopy__(sh, name, data, hash_name(name));
}

struct shash_node *
shash_add(struct shash *sh, const char *name, const void *data)
{
    return shash_add_nocopy(sh, xstrdup(name), data);       // 为key申请内存空间
}

删除节点

void
shash_delete(struct shash *sh, struct shash_node *node)
{
    free(shash_steal(sh, node));
}

char *
shash_steal(struct shash *sh, struct shash_node *node)
{
    char *name = node->name;

    hmap_remove(&sh->map, &node->node);
    free(node);
    return name;
}

查找节点

static struct shash_node *
shash_find__(const struct shash *sh, const char *name, size_t name_len,
             size_t hash)
{
    struct shash_node *node;

    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &sh->map) {
        if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
            return node;
        }
    }
    return NULL;
}

struct shash_node *
shash_find(const struct shash *sh, const char *name)
{
    return shash_find__(sh, name, strlen(name), hash_name(name));
}

遍历所有节点

#define SHASH_FOR_EACH(SHASH_NODE, SHASH)                               \
    HMAP_FOR_EACH_INIT (SHASH_NODE, node, &(SHASH)->map,                \
                        BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \
                        BUILD_ASSERT_TYPE(SHASH, struct shash *))

#define SHASH_FOR_EACH_SAFE(SHASH_NODE, NEXT, SHASH)        \
    HMAP_FOR_EACH_SAFE_INIT (                               \
        SHASH_NODE, NEXT, node, &(SHASH)->map,              \
        BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \
        BUILD_ASSERT_TYPE(NEXT, struct shash_node *),       \
        BUILD_ASSERT_TYPE(SHASH, struct shash *))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1001014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

卡尔曼滤波公式推导(总结)

假设 小车在t时刻的初始状态可以用Pt&#xff08;当前位置&#xff09;&#xff0c;Vt&#xff08;当前速度&#xff09;&#xff0c;Ut表示加速度&#xff1a; 预测&#xff1a; 利用上一个时刻的旧状态和系统的动量模型&#xff08;如加速度&#xff0c;速度等&#xff09;…

Android逆向——脱壳解析

“壳”是一种对程序进行加密的程序&#xff0c;“壳”形象地表现了这个功能。我们可以把被加壳的程序当成食物&#xff0c;而加壳程序就是在外面加上一层坚硬的外壳&#xff0c;防止别人去窃取其中的程序。加壳后的程序依然可以被直接运行。在程序运行时壳的代码先运行&#xf…

修改hosts文件无权限解决办法

文章目录 一、问题描述二、解决办法 一、问题描述 当我们在hosts文件中添加内容后----->点击文件右上角X按钮---->点击保存 系统会弹出我们没有权限的问题 二、解决办法 在搜索栏中搜索cmd---->右键命令提示符---->点击以管理员身份运行 输入cd C:\Windows\Sy…

文件上传漏洞(CVE-2022-30887)

简介 多语言药房管理系统&#xff08;MPMS&#xff09;是用PHP和MySQL开发的&#xff0c;该软件的主要目的是在药房和客户之间提供一套接口&#xff0c;客户是该软件的主要用户。该软件有助于为药房业务创建一个综合数据库&#xff0c;并根据到期、产品等各种参数提供各种报告…

OpenMMLab MMYOLO目标检测环境搭建(一)

1、环境搭建 conda create -n mmyolo python3.7 -y #创建环境 conda activate mmyolo #激活环境 conda install pytorch torchvision torchaudio cudatoolkit10.2 -c pytorch #安装 PyTorch and torchvision (官方)#如果网不好&#xff0c;可以这样安装 pi…

KNN算法回归问题介绍和实现

上篇博客中&#xff0c;介绍了使用KNN算法实现分类问题&#xff0c;本篇文章介绍使用KNN算法实现回归问题。介绍思路是先使用sklearn包提供的方法实现一个KNN算法的回归问题。再自定义实现一个KNN算法的回归问题工具类。 一、sklearn包使用KNN算法 1. 准备数据 使用sklearn包…

【C++基础】观察者模式(“发布-订阅”模式)

本文参考&#xff1a;观察者模式 - 摩根斯 | 爱编程的大丙 观察者模式允许我们定义一种订阅机制&#xff0c;可在对象事件发生时通知所有的观察者对象&#xff0c;使它们能够自动更新。观察者模式还有另外一个名字叫做“发布-订阅”模式。 发布者&#xff1a; 添加订阅者&…

【数据关联(1)】Tracking-by-detection 多目标跟踪范式与“数据关联”的关系说明

这个领域有一些专有名词需要大家清楚&#xff01; 文章目录 1 Tracking-by-detection multi-object tracking(MOT) 范式跟踪器是什么&#xff1f;1.1 关系图&#xff08;个人理解&#xff0c;如有错误请指正&#xff09;1.2 跟踪器有哪些&#xff1f; 2、核心部分“数据关联”…

Python格式化字符串(格式化输出)

print() 函数的用法&#xff0c;这只是最简单最初级的形式&#xff0c;print() 还有很多高级的玩法&#xff0c;比如格式化输出&#xff0c;这就是本节要讲解的内容。 熟悉C语言 printf() 函数的读者能够轻而易举学会 Python print() 函数&#xff0c;它们是非常类似的。 pri…

SegNetr: 重新思考 U 形网络中的局部-全局交互和跳过连接

SegNetr 会议分析摘要贡献方法整体框架1. SegNetr Block2.Information Retention Skip Connection 实验1.对比实验2.消融实验2.1 Effect of local-global interactions.2.2 Effect of patch size2.3 Effect of IRSC 可借鉴参考 会议分析 论文出处&#xff1a; arXiv预印版 除了…

C++在C语言基础上的优化

目录 一、命名空间 1、命名空间的定义 2、命名空间的使用 二、输入&输出 三、缺省参数 1、缺省参数的概念 2、缺省参数的分类 四、函数重载 五、引用 1.引用的概念 2.引用的特性 3、引用和指针的区别 六、内联函数 七、基于范围的for循环 一、命名空间 命名空…

计算机网络第六章——应用层(下)

等闲变却故人心&#xff0c;却道故人心易变 文章目录 用户代理就是用户和电子邮件系统之间的一个接口&#xff0c;通常都是运行在电脑中的一个程序&#xff0c;用户代理又可以称为电子邮件客户端软件&#xff0c;用户代理可以为用户提供一个比较友好的接口&#xff0c;邮件服务…

详解初阶数据结构之顺序表(SeqList)——单文件实现SeqList的增删查改

目录 一、线性表 二、顺序表 2.1概念及结构 2.2接口实现 2.3动态顺序表的创建 2.3动态顺序表的初始化 2.3.1传值初始化 2.3.2传址初始化 2.4动态顺序表的清空 2.5动态顺序表的扩容 2.6动态顺序表内容的打印 三、动态顺序表的使用 3.1尾插尾删 3.1.1尾插 3.1.2尾删…

集合框架和泛型二

一、Set接口 1. Set接口概述 java.util.Set 不包含重复元素的集合、不能保证存储的顺序、只允许有一个 null。 public interface Set<E> extends Collection<E>抽象方法&#xff0c;都是继承自 java.util.Collection 接口。 Set 集合的实现类有很多&#xff0c;…

分布式AKF拆分原则

目录 1 前言2 什么是AKF3 如何基于 AKF X 轴扩展系统&#xff1f;4 如何基于 AKF Y 轴扩展系统&#xff1f;5 如何基于 AKF Z 轴扩展系统&#xff1f;6 小结 1 前言 当我们需要分布式系统提供更强的性能时&#xff0c;该怎样扩展系统呢&#xff1f;什么时候该加机器&#xff1…

网络安全(黑客)技术自学

前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防…

高忆管理:突破22万亿!五大保险巨头总资产创历史新高

阅历了几年深度转型的稳妥业正在冲破迷雾。 券商我国记者统计显现&#xff0c;本年上半年&#xff0c;我国人寿、我国人保、我国安全、我国太保、新华稳妥等五大A股上市险企总财物破22万亿元&#xff0c;半年度营收1.5万亿元。从3至5年中长周期来看&#xff0c;稳妥集团公司体…

【大数据】Kafka 入门指南

Kafka 入门指南 1.Kafka 简介2.Kafka 架构3.分区与副本4.偏移量5.消费者组6.总结 1.Kafka 简介 Apache Kafka 是一种高吞吐、分布式的流处理平台&#xff0c;由 LinkedIn 开发并于 2011 年开源。它具有 高伸缩性、高可靠性 和 低延迟 等特点&#xff0c;因此在大型数据处理场景…

“JSR303和拦截器在Java Web开发中的应用与实践“

目录 引言JSR303什么是JSR303?为什么要使用JSR303?常用注解快速入门JSR303 拦截器什么是拦截器拦截器与过滤器应用场景快速入门拦截器 总结 引言 在Java Web开发过程中&#xff0c;我们经常会遇到需要对输入数据进行验证和处理&#xff0c;同时需要对请求进行拦截与控制的需…

纷享销客受邀出席CDIE2023数字化创新博览会 助力大中型企业增长

2023年&#xff0c;穿越周期&#xff0c;用数字化的力量重塑企业经营与增长的逻辑&#xff0c;再次成为企业数字化技术应用思考的主旋律&#xff0c;以数字经济为主线&#xff0c;数字技术融入产业发展与企业增长为依据&#xff0c;推动中国企业数字化升级。 9月5日&#xff0c…