# 2024q1 Homework2 (quiz1+2)
contributed by < [jason50123](https://github.com/jason50123) >
[作業要求](https://hackmd.io/@sysprog/BkmmEQCn6)
## 第一次測驗
### Test 1:
### Test 2:Tim sort
## 第二次測驗
### Test 1: Construct Binary Tree from Preorder and Inorder Traversal
在實作過程中,嘗試寫出 `hlist_add_head` function 的過程中發現,自己對於`**pprev` 存的東西不是很了解,經過思考後終於理解裡面是存放 `前一個點的 next poineter 的 address`。
以及在理解程式碼中,對於為何要使用 `hash function` 來完成感到很疑惑,但後來才發現,如果我不用 `hash function` 的話,在 `dfs` 呼叫 `find node` 的時候,會需要花 *O(n)* 的時間去 traverse 整條 array ,但如果今天用 hash 的話就只需花 *O(1)* 的時間就可以完成
[build tree practice](https://github.com/jason50123/build_tree_testing.git)
### linux kernel Cgroups research
在 linux kernel 的 `cgroup.h` 中我們可以找到以下的macro是有關 pre-order 的 traversal
```c
#define css_for_each_descendant_pre(pos, css) \
for ((pos) = css_next_descendant_pre(NULL, (css)); (pos); \
(pos) = css_next_descendant_pre((pos), (css)))
```
由最一開始的 macro 我們可以看到他會去呼叫 `css_next_descendant_pre()` 這個 function
為了 trace `cgroups` 中的 `css_next_descendant_pre()` function 要先簡單看一下 `cgroup_subsys_state` 這個 structure,裡面包含重要的`list_head *sibling` 會存 parent node 的 children,以及`list_head *children` 存自己的子節點。
```graphviz
digraph structs {
rankdir=LR;
node [shape=record];
subgraph cluster1{
label="cgroup_subsys_state";
struct1[label= "parent"];
struct2[label="sibling
|{prev|next}"];
struct3[label="children
|{prev|next}"];
}
}
```
接下來就可以從下面的 function 看到會先從 root 開始走,然後接著會去 call `css_next_child`確認說是否有 child,若有就往子節點去traverse,若沒有的話,就是往當前節點的 sibling 去走.
```c
struct cgroup_subsys_state *
css_next_descendant_pre(struct cgroup_subsys_state *pos,
struct cgroup_subsys_state *root)
{
struct cgroup_subsys_state *next;
cgroup_assert_mutex_or_rcu_locked();
/* if first iteration, visit @root */
if (!pos)
return root;
/* visit the first child if exists */
next = css_next_child(NULL, pos);
if (next)
return next;
/* no child, visit my or the closest ancestor's next sibling */
while (pos != root) {
next = css_next_child(pos, pos->parent);
if (next)
return next;
pos = pos->parent;
}
return NULL;
}
```
節錄`css_next_child`中的片段,其中的`likely(!(pos->flags & CSS_RELEASED)` 是拿來看說 pos 這個點是不是已經從 list 中被移除了,如果是的話就會`CSS_RELEASED` 就會被設為 true , 而此時我們就會直接跳過該點,並直接去 traverse 該點的 sibling。
```c
struct cgroup_subsys_state *css_next_child(struct cgroup_subsys_state *pos,
struct cgroup_subsys_state *parent)
{
...
} else if (likely(!(pos->flags & CSS_RELEASED))) {
next = list_entry_rcu(pos->sibling.next, struct cgroup_subsys_state, sibling);
}
...
return NULL;
}
```
### Test2
在 `linux/mmzone.h` 下我們可以找到有關 LRU 的應用。當系統中的`physical page` 低於設定的 `page_min` 、 `page_low` 的值,就會將某些 deactivate 的 page 進行回收。
舊版的 linux kernel 會透過 `__pagevec_lru_add` 一路呼叫到`add_page_to_lru_list` 來把 page 加到相對的lru_list中,但在最新的linux kernel版本,則是透過 folio 來把原本的 struct page 包起來,並透過`lruvec_add_folio` 來完成這樣的動作。
```c
/**
* folio_add_lru - Add a folio to an LRU list.
* @folio: The folio to be added to the LRU.
*
* Queue the folio for addition to the LRU. The decision on whether
* to add the page to the [in]active [file|anon] list is deferred until the
* folio_batch is drained. This gives a chance for the caller of folio_add_lru()
* have the folio added to the active list using folio_mark_accessed().
*/
void folio_add_lru(struct folio *folio)
{
struct folio_batch *fbatch;
VM_BUG_ON_FOLIO(folio_test_active(folio) &&
folio_test_unevictable(folio), folio);
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
/* see the comment in lru_gen_add_folio() */
if (lru_gen_enabled() && !folio_test_unevictable(folio) &&
lru_gen_in_fault() && !(current->flags & PF_MEMALLOC))
folio_set_active(folio);
folio_get(folio);
local_lock(&cpu_fbatches.lock);
fbatch = this_cpu_ptr(&cpu_fbatches.lru_add);
folio_batch_add_and_move(fbatch, folio, lru_add_fn);
local_unlock(&cpu_fbatches.lock);
}
```
### Test3