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