--- tags: Linux Kernel --- # 2023q1 Homework3 (quiz3) contributed by < [`chun61205`](https://github.com/chun61205) > > [2023q1 第 3 週測驗題](https://hackmd.io/@sysprog/linux2023-quiz3) ## 測驗一 ### 研讀 data alignment #### Data Alignment data alignment 的意思是, data 的 address 可以公平的被 $1,2,4,8$ ,這些數字整除,也就是 $2$ 的 $n$ 次方 ($n\in N$)。換句話說,這些 data object 可以用 $1,2,4,8$ byte 去 alignment 。 #### CPU 資料存取 CPU 會根據電腦系統架構決定一次要存取的 byte 數,32 位元的 cpu 一次可以讀取 32 bit 的資料,64 位元一次可以讀取 64 bit 。 ![](https://i.imgur.com/17AfP43.png) ![](https://i.imgur.com/r6aGw6e.png) 因此,如果定義個了一個 struct : ```c struct s1 { char c; int a; } ``` 原本推論 char 為 $1$ byte,而 int 為 $4$ byte ,兩個加起來應該為 $5$ byte,然而實際上為 $8$ byte,由於 int 為 $4$ byte ,所以 char 為了要能 alignment $4$ byte 就多加了 $3$ byte 進去 ,使得 cpu 存取速度不會因 address 不是在 $4$ 的倍數上,存取變慢。 ### 研讀 [`treesort.c`](https://gist.github.com/jserv/920766d1fd893d0efd66ac7fdaf23401) #### 節點 ```c typedef struct __node { uintptr_t color; struct __node *left, *right; struct __node *next; long value; } node_t __attribute__((aligned(sizeof(long)))); ``` * 該資料結構 `node_t` 紀錄了紅黑數的節點,包括了 `color` 、左右節點 `left` 和 `right` ,在原本陣列中的下一個節點 `next` ,和其值 `value` 。 * 其中顏色的定義如下: ```c typedef enum { CMAP_RED = 0, CMAP_BLACK } color_t; ``` 紅色是 `0` ,黑色則是 `1` 。 * 值得注意的是,這裡的 `color` 同時拿來儲存 parent 的位置和當前節點的 color。因為指標的 alignment 方式是 $8$ bytes ,也就是指標的地址必須是 $8$ 的倍數,換句話說 `node_t` 的指標的最後 $3$ 個 bit 必定是 $0$ ($3$ bits 最高能表示到 $7$ ,因此最後 $3$ 個 bit 必定用不到)。 因此可以判斷: * 最後一個 bit 可以拿來儲存 color ,且只要把最後 $3$ bit 設成 `0` ,剩下的就是 parent 的位置。 ```c #define rb_parent(r) ((node_t *) (((r)->color & ~7))) #define rb_color(r) ((color_t) (r)->color & 1) ``` * 設定位置的話則需要考慮到當前節點的顏色 ```c #define rb_set_parent(r, p) \ do { \ (r)->color = rb_color(r) | (uintptr_t) (p); \ } while (0) ``` * 設定顏色只要針對 `color` 的最後一個 bit 操作就好。 ```c #define rb_set_red(r) \ do { \ (r)->color &= ~1; \ } while (0) #define rb_set_black(r) \ do { \ (r)->color |= 1; \ } while (0) ``` #### `cmap_internal` ```c struct cmap_internal { node_t *head; /* Properties */ size_t key_size, element_size, size; cmap_iter_t it_end, it_most, it_least; int (*comparator)(void *, void *); }; ``` 該資料結構 `cmap_internal` 用以紀錄 `map` 的資料。 #### `tree_sort` :::spoiler 完整程式碼 ```c void tree_sort(node_t **list) { node_t **record = list; cmap_t map = cmap_new(sizeof(long), sizeof(NULL), cmap_cmp_int); while (*list) { cmap_insert(map, *list, NULL); list = &(*list)->next; } node_t *node = cmap_first(map), *first = node; for (; node; node = cmap_next(node)) { *list = node; list = &(*list)->next; } *list = NULL; *record = first; free(map); } ``` ::: 這裡的 `tree_sort` 從 `list` 一個一個讀取節點,並插入到紅黑樹,隨後再利用 `cmp_next` 將排序好的節點一個一個接在 `list` 。 --- ## 測驗二 ### 解釋程式碼原理 #### 資料結構 ```c struct avl_node { unsigned long parent_balance; struct avl_node *left, *right; } AVL_NODE_ALIGNED; enum avl_node_balance { AVL_NEUTRAL = 0, AVL_LEFT, AVL_RIGHT, }; static inline struct avl_node *avl_parent(struct avl_node *node) { return (struct avl_node *) node->parent_balance & ~3; } static inline enum avl_node_balance avl_balance(const struct avl_node *node) { return (enum avl_node_balance)node->parent_balance & 3; } ``` 從以上程式碼可以觀察到, avl tree 有三種狀態,標為 `AVL_NEUTRAL`, `AVL_LEFT`, `AVL_RIGHT` 。此外,這段程式碼也有利用 data alignment 的原理,將 `parent_balence` 的最後兩位元當作該節點的狀態。 --- ## 測驗三 ## 測驗四 ### 解釋程式碼原理 ```c r = (x > 0xFFFF) << 4; x >>= r; shift = (x > 0xFF) << 3; x >>= shift; shift = (x > 0xF) << 2; x >>= shift; shift = (x > 0x3) << 1; x >>= shift; ``` 這幾段程式碼原理是,若是 `x` 的前 $16, 24, 28, 30$ 個 bits 不全為 `0` 則分別向右移 $16, 8, 4, 2$。換句話說就是利用 binary search 來找到 MSB。 這些總共 shift 的位數會全部被累加到 `r` ,用來表示 $log(x)$ 的整數部份。 ```c r |= shift; ``` 之所以能夠使用 `|=` 是因為這些 shift 的位數全部都是 2 的冪。 最後在執行這段程式碼時 ```c return (KKKK) + 1; ``` 1. `r` 必須先加上 `shift` (`r | shift`) 。 2. `x` 最高會有兩位數因此需要做和上面一樣的操作 (`x >> 1`) 。 3. 最後,考慮到 `x = 0b01` 的情況,必須再加上 `1` 。 因此 `KKKK` 為 `(r | shift | x >> 1)`