open vswitch研究:基本数据结构

hmap

/* A hash map. */
struct hmap {
    struct hmap_node **buckets; /* Must point to 'one' iff 'mask' == 0. */
    struct hmap_node *one;
    size_t mask;
    size_t n;
};

/* A hash map node, to be embedded inside the data structure being mapped. */
struct hmap_node {
    size_t hash;                /* Hash value. */
    struct hmap_node *next;     /* Next in linked list. */
};

一个struct hmap_node* 的list是由相同hash的hmap_node组成,用*buckets来指向,buckets是struct hmap_node*的指针数组。其中buckets数组的第一个指针初始化存放在struct hmap_node* one里面。mask表示哈希值的取模,通过hash & mask得到hmap_node所在的buckets下标索引。n表示hmap里当前所有struct hmap_node的个数。一般来说,hmap的node个数不应该超过mask的2倍,如果超过了,需要调整mask值。


sset

sset是string的hmap

/* A set of strings. */
struct sset {
    struct hmap map;
};

struct sset_node {
    struct hmap_node hmap_node;
    char name[1];
};

sset_init, sset_destroy, sset_clear, sset_delete, 

sset_delete,删除sset的hmap对应的hmap_node,sset_clear对hmap删除所有hmap_node,sset_destroy删除所有hmap_node之后hmap_destroy整个hash map,sset_init初始化hmap

sset_find__,在sset的hmap中查找name对应的hmap_node

sset_add__,xmalloc一个sset_node结构,调用hmap_insert插入到sset的hmap中

SSET_FOR_EACH_SAFE宏用来遍历sset,SSET_NODE_FROM_HMAP_NODE, SSET_NAME_FROM_HMAP_NODE, SSET_NODE_NAME用来找到sset的某个hmap_node

SSET_FIRST, SSET_NEXT,用来找sset的第一个node, 下一个node


shash

shash和sset基本没啥区别,唯一区别在于sset是一个string的hmap,shash是一个key-value的hmap

struct shash {                                                                                        
    struct hmap map;                                                                                  
};           

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

shash的操作和sset基本一致,就不多说了,无非是hmap的操作的封装


simap

这是一个string 2 integer的hmap数据结构

/* A map from strings to unsigned integers. */
struct simap {
    struct hmap map;            /* Contains "struct simap_node"s. */
};


struct simap_node {
    struct hmap_node node;      /* In struct simap's 'map' hmap. */
    char *name;
    unsigned int data;
};


ofpbuf

struct ofpbuf {
    void *base;                 /* First byte of allocated space. */
    size_t allocated;           /* Number of bytes allocated. */
    enum ofpbuf_source source;  /* Source of memory allocated as 'base'. */

    void *data;                 /* First byte actually in use. */
    size_t size;                /* Number of bytes in use. */

    void *l2;                   /* Link-level header. */
    void *l3;                   /* Network-level header. */
    void *l4;                   /* Transport-level header. */
    void *l7;                   /* Application data. */

    struct list list_node;      /* Private list element for use by owner. */
    void *private_p;            /* Private pointer for use by owner. */
};

ofpbuf是一种内存管理类,从名字上看,是给openflow protocol用的,可以通过list_node,把很多ofpbuf挂到一个list上,其成员enum ofpbuf_source source表示这段内存的性质,目前定义了3种,

enum ofpbuf_source {
    OFPBUF_MALLOC,              /* Obtained via malloc(). */
    OFPBUF_STACK,               /* Un-movable stack space or static buffer. */
    OFPBUF_STUB                 /* Starts on stack, may expand into heap. */
};

OFPBUF_MALLOC表示这段内存是malloc分配的,所以释放的时候要调用free; OFPBUF_STACK表示这段内存是栈空间; OFPBUF_STUB表示这段内存是栈空间,但可能会溢出到堆空间里(我汗。。哥们你搞笑呢?)

ofpbuf_use 基于malloc分配的内存创建一个ofpbuf结构,ofpbuf_use_stack,基于一块栈的内存创建ofpbuf结构,两者都调用了ofpbuf_use__,该函数把使用的size设置为0,表示这是一段未使用的内存。另一种创建ofpbuf的方式ofpbuf_use_const,把size设置为allocated的大小,说明内存已经用满。

ofpbuf_init, ofpbuf_uninit,用malloc/free分配释放一块内存

和skb一样,ofpbuf也有类似的指针,ofpbuf->base表示内存的开始位置,ofpbuf->data表示数据包的开始位置,ofpbuf_end返回base+allocated位置,表示内存的结束位置,ofpbuf_tail返回data+size位置,表示数据的结束位置。由此,ofpbuf_headroom返回从base到data之间的空间大小,ofpbuf_tailroom返回end到tail之间的空间大小。

ofpbuf_resize__,为当前数据包预留new_headroom长度的头部空间,和new_tailroom长度的尾部空间。该函数会重新分配一段内存,大小为size + new_headroom + new_tailroom,把原有数据拷贝到新内存,并相应修改base, data, l2, l3, l4, l7的位置。数据拷贝调用ofpbuf_copy__完成。ofpbuf_trim做相反的操作,把headroom, tailroom截断为0

ofpbuf_prealloc_tailroom/ofpbuf_prealloc_headroom,如果当前headroom/tailroom空间不满足要求,调用ofpbuf_resize__重新分配headroom/tailroom(至少64字节)

ofpbuf_new_with_headroom,分配一段size + headroom大小的内存,之后ofpbuf->data = ofpbuf->base  + size

ofpbuf_put_uninit,调用ofpbuf_prealloc_tailroom为内存段保留size大小的tailroom,返回新的tail位置。该函数有多个衍生函数: 

ofpbuf_put_zeros,除ofpbuf_put_uninit之外,把tail之后的size大小的内存空间置0; ofpbuf_put,除ofpbuf_put_uninit之外,拷贝一段size大小的数据; 

和skb结构体一样,ofpbuf也有push和pull的操作

ofpbuf_push_uninit,调用ofpbuf_prealloc_headroom为内存段保留size大小的headroom,之后数据往前长占用headroom的size个字节,此时headroom应该为0. 这种行为和skb的push基本一致。ofpbuf_push_zeros, ofpbuf_push为其衍生函数

ofpbuf_pull,把data往后移size个字节,相当于给headroom空出size个字节空间


flow

lib/flow.h lib/flow.c是用户态视角的flow定义,该结构和内核态的sw_flow完全不同,其真正包含了flow的各个field的内容,e.g.

struct flow {
    ovs_be64 tun_id;            /* Encapsulating tunnel ID. */
    struct in6_addr ipv6_src;   /* IPv6 source address. */
    struct in6_addr ipv6_dst;   /* IPv6 destination address. */
    struct in6_addr nd_target;  /* IPv6 neighbor discovery (ND) target. */
    uint32_t skb_priority;      /* Packet priority for QoS. */
    uint32_t regs[FLOW_N_REGS]; /* Registers. */
    ovs_be32 nw_src;            /* IPv4 source address. */
    ovs_be32 nw_dst;            /* IPv4 destination address. */
    ovs_be32 ipv6_label;        /* IPv6 flow label. */
    uint16_t in_port;           /* OpenFlow port number of input port. */
    ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
    ovs_be16 dl_type;           /* Ethernet frame type. */
    ovs_be16 tp_src;            /* TCP/UDP source port. */
    ovs_be16 tp_dst;            /* TCP/UDP destination port. */
    uint8_t dl_src[6];          /* Ethernet source address. */
    uint8_t dl_dst[6];          /* Ethernet destination address. */
    uint8_t nw_proto;           /* IP protocol or low 8 bits of ARP opcode. */
    uint8_t nw_tos;             /* IP ToS (including DSCP and ECN). */
    uint8_t arp_sha[6];         /* ARP/ND source hardware address. */ 
    uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
    uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
    uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
    uint8_t reserved[2];        /* Reserved for 64-bit packing. */
};

flow_extract,解析ofpbuf里的skb数据包,并基于skb结构形成flow,其中需要确保skb的线性空间至少要包含tcp头和之前的所有内容。和flow_extract相反的操作flow_compose,基于一个struct flow构造一个skb数据包的ofpbuf出来

struct flow_wildcards {
    ovs_be64 tun_id_mask;       /* 1-bit in each significant tun_id bit. */
    flow_wildcards_t wildcards; /* 1-bit in each FWW_* wildcarded field. */
    uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
    ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
    ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
    struct in6_addr ipv6_src_mask; /* 1-bit in each signficant ipv6_src bit. */
    struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
    struct in6_addr nd_target_mask; /* 1-bit in each significant
                                       nd_target bit. */
    ovs_be16 vlan_tci_mask;     /* 1-bit in each significant vlan_tci bit. */
    ovs_be16 tp_src_mask;       /* 1-bit in each significant tp_src bit. */
    ovs_be16 tp_dst_mask;       /* 1-bit in each significant tp_dst bit. */
    uint8_t nw_frag_mask;       /* 1-bit in each significant nw_frag bit. */
    uint8_t zeros[5];           /* Padding field set to zero. */
};

flow_wildcards用来做flow match的wildcard,wildcard表示匹配任意数值。对于各个mask的bit而言,bit为0表示这位被wildcard,match flow的时候被无视掉,bit 1则表示该bit需要被匹配

flow_wildcards_init_catchall,初始化struct flow_wildcards结构,使得可以匹配任意的flow。可以看出该函数首先把flow_wildcards->wildcards = FWW_ALL,之后设置所有mask为0

flow_wildcards_init_exact,这样初始化的wildcard,不会去wildcard任何的flow中的field or bit,也就是屏蔽wildcard功能。该函数把flow_wildcards->wildcards = 0,之后设置所有mask位为1

flow_wildcards_combine,合并src1, src2两个struct flow_wildcards*,对于flow_wildcards->wildcards,执行 | 操作,对于flow_wildcards其他field,执行 & 操作

flow_zero_wildcards,对flow的每个field,和flow_wildcards的相应field做 & 操作


dynamic-string

/* A "dynamic string", that is, a buffer that can be used to construct a
 * string across a series of operations that extend or modify it.
 *
 * The 'string' member does not always point to a null-terminated string.
 * Initially it is NULL, and even when it is nonnull, some operations do not
 * ensure that it is null-terminated.  Use ds_cstr() to ensure that memory is
 * allocated for the string and that it is null-terminated. */
struct ds {
    char *string;       /* Null-terminated string. */
    size_t length;      /* Bytes used, not including null terminator. */
    size_t allocated;   /* Bytes allocated, not including null terminator. */
};

ds类似于c++里的string类

ds_reserve(struct ds* ds, size_t length),调用xrealloc重新分配ds->allocated + MAX(ds->allocated, length)长度的内存,这表明每次ds_reserve,ds至少要增加一倍的长度

ds_put_uninit(struct ds* ds, size_t n),追加ds一个n个字节长度,返回第ds->length + 1个字节的地址

ds_truncate, ds_clear,仅仅修改ds->length的长度即可

ds_put_cstr(struct ds* ds, const char* s),把string s追加到ds尾部

ds_put_format_valist(struct ds* ds, const char* format, va_list args_),把按照format格式和va_list参数的字符串追加到ds尾部

ds_get_line,从文件里循环读字符,遇到EOF或者\n则停止,返回一行的内容

ds_cstr,给ds->string[ds->length] = '\0',返回ds->string


json

/* Type of a JSON value. */
enum json_type {
    JSON_NULL,                  /* null */
    JSON_FALSE,                 /* false */
    JSON_TRUE,                  /* true */
    JSON_OBJECT,                /* {"a": b, "c": d, ...} */
    JSON_ARRAY,                 /* [1, 2, 3, ...] */
    JSON_INTEGER,               /* 123. */
    JSON_REAL,                  /* 123.456. */
    JSON_STRING,                /* "..." */
    JSON_N_TYPES
};

json的数据结构如下

/* A JSON array. */
struct json_array {
    size_t n, n_allocated;
    struct json **elems;
};

/* A JSON value. */
struct json {
    enum json_type type;
    union {
        struct shash *object;   /* Contains "struct json *"s. */
        struct json_array array;
        long long int integer;
        double real;
        char *string;
    } u;
};

struct json_array对应json里的线性数据结构,[], {}, ()都是线性数据结构,但目前只用到[]。可以看到struct json_array是一个struct json* 的数组,大小为n_allocated,长度为n

struct json根据type不同对应的数据结构也不同,struct shash* object对应json dict,struct json_array是线性数组,这两个都是容器结构;剩下的就是integer, real, string了

static struct json *
json_create(enum json_type type)
{
    struct json *json = xmalloc(sizeof *json);
    json->type = type;
    return json;
}   

如果创建指定类型的json元素,e.g. json_boolean_create, json_string_create, json_array_create_empty, 都是调用json_create

对于json_array而言,通过json_array_add, json_array_trim, json_array_create来增减元素。对于json_object而言,json_object_put用来添加哈希表的元素。

json_destroy用来释放一个json结构,对于boolean, integer, real而言,只需free(json)即可,对于string还需free(json->u.string),对于object和array复杂一点,json_destroy_array对于每个element,递归调用json_destroy;json_destroy_object对于哈希表每个key<->value,递归调用json_destroy,最后释放整个shash占用的内存

json_clone,对于原子数据结构(e.g. integer, real, string)而言,调用json_xxx_create。对于JSON_OBJECT,调用json_clone_object,该函数把老object的每个元素顺序通过json_object_put到新的object里,对于JSON_ARRAY,调用json_clone_array,该函数把老array的每个元素放到struct json** elems中,通过json_array_create返回。


json的hash值计算函数如下:

size_t json_hash(const struct json *json, size_t basis)
{
    switch (json->type) {
    case JSON_OBJECT:
        return json_hash_object(json->u.object, basis);

    case JSON_ARRAY:
        return json_hash_array(&json->u.array, basis);

    case JSON_STRING:
        return hash_string(json->u.string, basis);

    case JSON_NULL:
    case JSON_FALSE:
    case JSON_TRUE:
        return hash_int(json->type << 8, basis);

    case JSON_INTEGER:
        return hash_int(json->u.integer, basis);

    case JSON_REAL:
        return hash_double(json->u.real, basis);

    case JSON_N_TYPES:
    default:
        NOT_REACHED();
    }
}

json_hash_object,首先对json object的数据结构struct shash进行排序,得到一个struct shash_node*的排序数组nodes,对nodes的每一个元素,循环计算hash值,每次计算的hash值都参与到后续hash值的计算中

static size_t
json_hash_object(const struct shash *object, size_t basis)
{
    const struct shash_node **nodes;
    size_t n, i;

    nodes = shash_sort(object);
    n = shash_count(object);
    for (i = 0; i < n; i++) {
        const struct shash_node *node = nodes[i];
        basis = hash_string(node->name, basis);
        basis = json_hash(node->data, basis);
    }
    return basis;
}       

json_hash_array和json_hash_object类似,对array每个元素调用json_hash


下面来看json的parse行为, parser器一个个把字符读进来分析,调用json_lex_input函数,该函数根据当前的parse状态决定下一步的行为,目前定义的状态有

enum json_lex_state {
    JSON_LEX_START,             /* Not inside a token. */
    JSON_LEX_NUMBER,            /* Reading a number. */
    JSON_LEX_KEYWORD,           /* Reading a keyword. */
    JSON_LEX_STRING,            /* Reading a quoted string. */
    JSON_LEX_ESCAPE             /* In a quoted string just after a "\". */
};

parser会顺序读下面的字符并存到一个dynamic-string结构里,除非遇到非法字符或者结束标记,e.g. 期望读数字结构读到逗号,读到字符串第二个引号,这时会调用json_lex_xxx,表示已经读完了一个json原子结构

json_lex_xxx,用来解析当前dynamic-string的内容,生成一个struct json_token之后,传给json_parser_input,目前的函数有json_lex_keyword,用来parse诸如true, false, null这样的常量,json_lex_number,以及json_lex_string。json_lex_string还要考虑反斜杠后面的特殊字符,所以会复杂些。

json_parser_input只接受object, array两类json容器开头的json字符串,如果是object,下面会调用json_parser_push_object,进而调用json_parser_push,如果当前json_parser->stack为空,让json_parser->stack[0]->json指向新的struct json*,否则调用json_parser_put_value

static void json_parser_put_value(struct json_parser *p, struct json *value)              
{                                                                             
    struct json_parser_node *node = json_parser_top(p);                       
    if (node->json->type == JSON_OBJECT) {                                    
        json_object_put(node->json, p->member_name, value);                   
        free(p->member_name);                                                 
        p->member_name = NULL;                                                
    } else if (node->json->type == JSON_ARRAY) {                              
        json_array_add(node->json, value);                                    
    } else {                                                                  
        NOT_REACHED();                                                        
    }                                                                         
}          

json_parser_top返回json_parser->stack最上端的struct json*,然后把key, value对存到该json node里面


我们以一个object的json为例,e.g. { "name" : "jerry", "score" : 100 }

假设该json由string传入,json_from_string会调用json_parser_create,生成一个struct json_parser结构,整个string2json的过程都会用到这个结构

/* A JSON parser. */                                                          
struct json_parser {                                                          
    int flags;                                                                
                                                                              
    /* Lexical analysis. */                                                   
    enum json_lex_state lex_state;                                            
    struct ds buffer;           /* Buffer for accumulating token text. */     
    int line_number;                                                          
    int column_number;                                                        
    int byte_number;                                                          
                                                                              
    /* Parsing. */                                                            
    enum json_parse_state parse_state;                                        
#define JSON_MAX_HEIGHT 1000                                                  
    struct json_parser_node *stack;                                           
    size_t height, allocated_height;                                          
    char *member_name;                                                        
                                                                              
    /* Parse status. */                                                       
    bool done;                                                                
    char *error;                /* Error message, if any, null if none yet. */
};                                        

enum json_parse_state parse_state状态代表了整个json解析过程的进展,表示期望的下一个字符,struct json_parser_node* stack是当出现了递归结构时,e.g. object的一个value又是一个object,把老的object压栈,先解析新的object


言归正传,json_from_string下面会调用json_parser_feed,该函数会一个个的读取字符,然后调用json_lex_input,我们的object第一个字符是{,其json_parser->lex_state初始化后为JSON_LEX_START, 此时可以得出当前token type为T_BEGIN_OBJECT,据此构造一个struct json_token,传给后续的json_parse_input

struct json_token {
    enum json_token_type type;
    union { 
        double real;
        long long int integer;
        const char *string;
    } u;
};      
            
enum json_lex_state {
    JSON_LEX_START,             /* Not inside a token. */
    JSON_LEX_NUMBER,            /* Reading a number. */
    JSON_LEX_KEYWORD,           /* Reading a keyword. */
    JSON_LEX_STRING,            /* Reading a quoted string. */
    JSON_LEX_ESCAPE             /* In a quoted string just after a "\". */
};

enum json_parse_state {
    JSON_PARSE_START,           /* Beginning of input. */
    JSON_PARSE_END,             /* End of input. */
    
    /* Objects. */
    JSON_PARSE_OBJECT_INIT,     /* Expecting '}' or an object name. */
    JSON_PARSE_OBJECT_NAME,     /* Expecting an object name. */
    JSON_PARSE_OBJECT_COLON,    /* Expecting ':'. */
    JSON_PARSE_OBJECT_VALUE,    /* Expecting an object value. */
    JSON_PARSE_OBJECT_NEXT,     /* Expecting ',' or '}'. */
            
    /* Arrays. */
    JSON_PARSE_ARRAY_INIT,      /* Expecting ']' or a value. */
    JSON_PARSE_ARRAY_VALUE,     /* Expecting a value. */
    JSON_PARSE_ARRAY_NEXT       

};

json_parser_input中,初始化的json_parser->parse_state为JSON_PARSE_START,此时期望接收的token为 { 或者 [ ,由于我们传入了 { , 会知道这是一个object,下面开始准备parse这个object,先把该object入栈,调用json_parser_push_object

json_parser_push_object 调用json_parser_push,即把一个struct json* 入栈到json_parser->stack里面,并把parse_state改为JSON_PARSE_OBJECT_INIT


下面继续json_parser_feed的主循环,下面json_lex_input会解析到开始字符" ,下面会开始连续尝试读取一个字符串,json_lex_input会一直被调用,直到读到结束的" 字符,此时调用json_lex_string,会生成一个T_STRING类型的struct json_token,传给json_parser_input

此时parse_state已经成为了JSON_PARSE_OBJECT_INIT,由于此时token类型不是 } ,代码很tricky的往下走,走到和JSON_PARSE_OBJECT_NAME,此时json_token的类型match到了T_STRING,此时为json_parser_push_object做准备,把json_parser->member_name 设为这个token->u.string,把json_parser->parse_state设为JSON_PARSE_OBJECT_COLON,表示期望接收一个冒号字符

下面自然就是读到一个冒号字符了,json_parser_intput会把状态变更为JSON_PARSE_OBJECT_VALUE,继续json_lex_input的循环,又会解析出一个字符串出来,在进入json_parser_input之后,调用json_parse_value,因为此时的字符串已经被当做value来处理了。

如果此时value是非容器类型,调用json_xxx_create生成一个struct json,再调用json_parser_put_value把这个json值放入栈顶的json里面。e.g. 如果栈顶是一个JSON_OBJECT,那么把member_name, value 组成的key-value对插入到这个JSON_OBJECT里,如果是一个JSON_ARRAY,那么就追加到数组末尾,最后把parse_state设为JSON_PARSE_OBJECT_NEXT


下面如果解析到的是, 字符,那么继续key-value对的解析,如果是 } 字符,说明object解析结束,此时调用json_parser_pop,此时这次解析的json结构已经存到了其父亲json的object/array里,所以此时弹出json_parser_top的struct json*是安全的


byteq





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值