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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章