# linux2023: nosba0957
## 測驗 α−1 解釋程式碼運作原理
`__treeint_dump()` 是使用中序 (in-order) 方式印出節點。
```c
__treeint_dump(st_left(n), depth + 1);
struct treeint *v = treeint_entry(n);
printf("%d\n", v->value);
__treeint_dump(st_right(n), depth + 1);
```
`treeint_remove()` 中先使用 `treeint_find()` 找到目標節點,再使用 `st_remove()` 刪除節點。`st_remove()` 透過 `st_replace_right()` 和 `st_replace_left()` 來刪除節點。若要刪除的節點有右子樹,則用右子樹的最小值取代。
用節點 `del` 擁有右子樹為例,要刪除 `del`,先找到其右子樹中最小值 `least`節點,再使用 `st_replace_right()` 把 `del` 節點換為 `least`,最後更新。
```c
void st_remove(struct st_node **root, struct st_node *del)
{
if (st_right(del)) {
struct st_node *least = st_first(st_right(del));
if (del == *root)
*root = least;
st_replace_right(del, least);
st_update(root, least);
return;
}
if (st_left(del)) {
struct st_node *most = st_last(st_left(del));
if (del == *root)
*root = most;
st_replace_left(del, most);
st_update(root, most);
return;
}
if (del == *root) {
*root = 0;
return;
}
/* empty node */
struct st_node *parent = st_parent(del);
if (st_left(parent) == del)
st_left(parent) = 0;
else
st_right(parent) = 0;
st_update(root, parent);
}
```
`st_update()` 的最後會比對更新後和更新前的 hint,若兩者不一樣則會往親代節點遞迴 `st_update()`。
```c
if (n->hint == 0 || n->hint != prev_hint)
st_update(root, p);
```
## 測驗 β-1 說明上述程式碼的運作原理
`alignment` 要為 2 的冪,所以 `mask` 是 2 進位中 0 到 n-1 位的遮罩。例如: alignment = 8,mask = 7 (0111 1111)。
`(sz + mask) & ~mask` 先加上 `mask`,可以讓沒有對齊的數往前到下一個區塊。
用老師的例子舉例,`sz` 可能的值為 120 ~ 123,`alignment` 為 4,`mask` 為 3。可以知道 121 ~ 123 沒有對齊,所以先加上 `mask`,值會變為 124 ~ 126,此時再和 `~mask` 做 bitwise and,即可將 2 進位中最高第 3 位以下的 bits 忽略。
```c
#include <stdint.h>
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) { /* power of two? */
return (sz + mask) & ~mask;
}
return (((sz + mask) / alignment) * alignment);
}
```
## 測驗 β-2 在 Linux 核心原始程式碼找出類似 align_up 的程式碼
在 [linux/tools/include/linux/mm.h](https://github.com/torvalds/linux/blob/374a7f47bf401441edff0a64465e61326bf70a82/tools/include/linux/mm.h#L15) 中找到類似的程式碼。舉例:[linux/mm/percpu.c](https://github.com/torvalds/linux/blob/374a7f47bf401441edff0a64465e61326bf70a82/mm/percpu.c#L1174)。
當 `index` 取得值後,使用 `__ALIGN_MASK(index, align_mask)` 來對齊,最後確認是否符合要求大小。
```c
static unsigned long pcpu_find_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned long nr,
unsigned long align_mask,
unsigned long *largest_off,
unsigned long *largest_bits)
{
unsigned long index, end, i, area_off, area_bits;
again:
index = find_next_zero_bit(map, size, start);
/* Align allocation */
index = __ALIGN_MASK(index, align_mask);
area_off = index;
end = index + nr;
if (end > size)
return end;
i = find_next_bit(map, end, index);
if (i < end) {
area_bits = i - area_off;
/* remember largest unused area with best alignment */
if (area_bits > *largest_bits ||
(area_bits == *largest_bits && *largest_off &&
(!area_off || __ffs(area_off) > __ffs(*largest_off)))) {
*largest_off = area_off;
*largest_bits = area_bits;
}
start = i + 1;
goto again;
}
return index;
}
```
## 測驗 γ-1 解釋上述程式碼運作原理
`qsort_thread()` 是讓 thread 執行的 function。在程式剛開始時,每個 thread 的狀態 `qs->st` 都會是 `ts_idle`,這裡先讓每個 thread 停在自己的 cond。
```c=340
again:
/* Wait for work to be allocated. */
verify(pthread_mutex_lock(&qs->mtx_st));
while (qs->st == ts_idle)
verify(pthread_cond_wait(&qs->cond_st, &qs->mtx_st));
verify(pthread_mutex_unlock(&qs->mtx_st));
if (qs->st == ts_term) {
return NULL;
}
assert(qs->st == ts_work);
```
最後只會剩一個 thread 在 `qsort_mt()`,他會用 signal 方式叫醒`pool[0]`。
```c=164
/* Hand out the first work batch. */
qs = &c.pool[0];
verify(pthread_mutex_lock(&qs->mtx_st));
qs->a = a;
qs->n = n;
qs->st = ts_work;
c.idlethreads--;
verify(pthread_cond_signal(&qs->cond_st));
verify(pthread_mutex_unlock(&qs->mtx_st));
```
當 `pool[0]` 的 thread 做完後,會再用 signal 的方式叫下一個停在 cond 上 thread 起來。
```c=353
verify(pthread_mutex_lock(&c->mtx_al));
qs->st = ts_idle;
c->idlethreads++;
if (c->idlethreads == c->nthreads) {
for (i = 0; i < c->nthreads; i++) {
qs2 = &c->pool[i];
if (qs2 == qs)
continue;
verify(pthread_mutex_lock(&qs2->mtx_st));
qs2->st = ts_term;
verify(pthread_cond_signal(&qs2->cond_st));
verify(pthread_mutex_unlock(&qs2->mtx_st));
}
verify(pthread_mutex_unlock(&c->mtx_al));
return NULL;
}
verify(pthread_mutex_unlock(&c->mtx_al));
```