---
tags: linux2022
---
# 2022q1 Homework1 (quiz1)
contributed by < [`yuwen`](https://github.com/yyyyuwen) >
> [作業要求](https://hackmd.io/@sysprog/B166rc3Jq)
## 測驗 1
這題主要是解釋了 Linux Kernal 中 hashmap 的實作,可以從講義得知定義為
```c=
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
struct hash_key {
int key;
void *data;
struct hlist_node node;
};
};
```
#### `hlist_head` 結構
* **只有 `first` ,並沒有 `pprev`** 。因為在使用 `hash table` 時並不會有針對 **tail** 的操作,且記憶體會減少很多。
```graphviz
digraph {
rankdir = LR;
node [shape=record];
subgraph cluster {
first;
label = "hlist_head";
}
}
```
#### `hlist_node` 結構
* `pprev` 是 **indirect pointer** ,因為如果和 `list_head` 一樣使用單純的指標的話在 delete node 時需要考慮 list 的 head 或是 NULL 兩種狀況,因此使用 `**pprev` 直接存取上一個 node 所在的位址。
```graphviz
digraph {
rankdir = LR;
node [shape=record];
subgraph cluster {
node0[label="{<p>pprev|<n>next}"];
label = "hlist_node";
}
}
```
#### `hash_key` 結構
```graphviz
digraph {
rankdir = LR;
node [shape=record];
subgraph cluster {
node0[label="key|data|{<p>pprev|<n>next}"];
label = "hash_key";
}
}
```
#### MAP_HASH_SIZE
這是在計算 hash table 的 size 。由 `MAP_HASH_SIZE()` 巨集來取得,值為輸入值乘以 2 ,並會紀錄在 `map_t` 的變數名稱 `bits` 中。
```c=
struct hlist_node { struct hlist_node *next, **pprev; };
struct hlist_head { struct hlist_node *first; };
typedef struct { int bits; struct hlist_head *ht; } map_t;
#define MAP_HASH_SIZE(bits) (1 << bits)
```
### map_init()
`map_init()` 的功能是為了動態配置出 table 結構,並將每個 hlist_head 的 first 成員都初始化為 NULL 。
```c=
map_t *map_init(int bits) {
map_t *map = malloc(sizeof(map_t));
if (!map)
return NULL;
map->bits = bits;
map->ht = malloc(sizeof(struct hlist_head) * MAP_HASH_SIZE(map->bits));
if (map->ht) {
for (int i = 0; i < MAP_HASH_SIZE(map->bits); i++)
(map->ht)[i].first = NULL;
} else {
free(map);
map = NULL;
}
return map;
}
```
```graphviz
digraph {
rankdir = LR;
node [shape=record];
subgraph cluster {
node0[label="bits (size of the hashtable)|<ht>ht"];
label = "map";
}
subgraph {
node[shape=plaintext, label=NULL]
NULL1, NULL2, NULL3, NULL4, NULL5
}
struct1 [label="<1>hlist_head.first|<2>...|<3>...|<4>...|<5>hlist_head.first" height=1.5]
node0:ht->struct1:1
struct1:1->NULL1
struct1:2->NULL2
struct1:3->NULL3
struct1:4->NULL4
struct1:5->NULL5
}
```
### find_key
`void *map_get` 可以透過 `static struct hash_key *find_key` 取得 `hash_key` 中的 address,最後回傳 address of data。
```c=
#define GOLDEN_RATIO_32 0x61C88647
static inline unsigned int hash(unsigned int val, unsigned int bits) {
/* High bits are more random, so use them. */
return (val * GOLDEN_RATIO_32) >> (32 - bits);
}
static struct hash_key *find_key(map_t *map, int key) {
struct hlist_head *head = &(map->ht)[hash(key, map->bits)];
for (struct hlist_node *p = head->first; p; p = p->next) {
struct hash_key *kn = container_of(p, struct hash_key, node);
if (kn->key == key)
return kn;
}
return NULL;
}
void *map_get(map_t *map, int key)
{
struct hash_key *kn = find_key(map, key);
return kn ? kn->data : NULL;
}
```
在 `find_key` 中的 `hash` function 是利用 **golden ratio marco** 來定義,參考 [tinyynoob](https://hackmd.io/bpf-14GARBu59uLiYwzChQ?both)同學的 g
### map_add