# 2023q1 Homework1 (lab0)
contributed by < [hbx1241](https://github.com/hbx1241) >
## 開發環境
```shell
$ gcc --version
gcc (GCC) 12.2.1 20230201
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 48 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 16
On-line CPU(s) list: 0-15
Vendor ID: AuthenticAMD
Model name: AMD Ryzen 7 5800U with Radeon Graphics
CPU family: 25
Model: 80
Thread(s) per core: 2
Core(s) per socket: 8
Socket(s): 1
Stepping: 0
Frequency boost: enabled
CPU(s) scaling MHz: 53%
CPU max MHz: 4505.0781
CPU min MHz: 1600.0000
BogoMIPS: 3792.95
```
## 開發過程
### q_free
```c
void q_free(struct list_head *l)
{
// Keep deleting first entry of the佇列until list is empty
while (l && !list_empty(l)) {
element_t *ptr = list_first_entry(l, element_t, list);
list_del(&ptr->list);
q_release_element(ptr);
}
// free head
free(l);
}
```
釋放佇列中第一個 `element_t` 直到佇列是空的,之後釋放 `l` 。
### q_insert_head
```c
bool q_insert_head(struct list_head *head, char *s)
{
if (!head || !s)
return false;
element_t *eptr = malloc(sizeof(element_t));
if (!eptr)
return false;
int string_size = strlen(s);
eptr->value = malloc(sizeof(char) * (string_size + 1));
if (!eptr->value) {
q_release_element(eptr);
return false;
}
strncpy(eptr->value, s, string_size + 1);
INIT_LIST_HEAD(&eptr->list);
list_add(&eptr->list, head);
return true;
}
```
利用 malloc 逐步配置 `element_t`, `value` 的記憶體,一旦失敗就釋放已經配置的記憶體並回傳 `false` ,否則將字串寫入 `eptr->value` 並初始化 `element_t` 中的 `list` ,將之加入佇列中。
### q_insert_tail
和 `q_insert_head` 一樣,但第 16 行改爲 `list_add_tail((&eptr->list, head));`
:::warning
從程式碼相似的狀況,思考進一步縮減程式碼的手段。
:notes: jserv
:::
### 新增指令
```c
struct list_head *q_new()
{
VALGRIND_PRINTF_BACKTRACE("hi");
struct list_head *p = malloc(sizeof(struct list_head));
if (!p)
return NULL;
else {
INIT_LIST_HEAD(p);
return p;
}
}
```
利用 `VALGRIND_PRINTF_BACKTRACE()` 得到執行 new 時的 call stack:
```c
==25082== by 0x10F298: q_new (queue.c:26)
==25082== by 0x10C968: do_new (qtest.c:150)
==25082== by 0x10DB0E: interpret_cmda (console.c:181)
==25082== by 0x10E0A2: interpret_cmd (console.c:201)
==25082== by 0x10EC9C: run_console (console.c:691)
==25082== by 0x10CF95: main (qtest.c:1299)
```
可以發現在 `console.c` 的 `interpret_cmda` 函式會比對 link list `cmd_list` 中是否有符合輸入的命令,比對到之後執行結構體 ` __cmd_element` 中 `operation` 所指向的函式;搜尋檔案中的 `cmd_list` 發現可以用 `ADD_COMMAND` 將指令加入 `cmd_list` 中並且根據 `console.h` :
```c
void add_cmd(char *name, cmd_func_t operation, char *summary, char *parameter);
#define ADD_COMMAND(cmd, msg, param) add_cmd(#cmd, do_##cmd, msg, param)
```
若輸入命令爲 `cmd` ,則建立一個 `do_cmd` 的函式
### 使用 Valgrind 檢查 qtest 記憶體流失
執行 `make valgrind` 可以觀察到程式執行結束後有如下訊息:
```c
==6016== 56 bytes in 1 blocks are still reachable in loss record 2 of 2
==6016== at 0x4841888: malloc (vg_replace_malloc.c:381)
==6016== by 0x10ED8F: test_malloc (harness.c:133)
==6016== by 0x10F162: q_new (queue.c:17)
==6016== by 0x10C968: do_new (qtest.c:150)
==6016== by 0x10DB0E: interpret_cmda (console.c:181)
==6016== by 0x10E0A2: interpret_cmd (console.c:201)
==6016== by 0x10E46E: cmd_select (console.c:610)
==6016== by 0x10ED1F: run_console (console.c:705)
==6016== by 0x10CF95: main (qtest.c:1299)
```
valgrind 官網的[常見問題](https://valgrind.org/docs/manual/faq.html#faq.deflost) 5.2 中提及:
> "still reachable" means your program is probably ok -- it didn't free some memory it could have.
因此可以判斷應該是程式結束前沒有完全釋放分配的記憶體; 嘗試執行 `valgrind ./qtest` 並輸入 `new` 命令後直接輸入 `quit` 也會發現相同理由的警告,但在 `quit` 之前執行 `free` 的話就會正常退出程式,符合從文件判斷的結果。
觀察 `qtest.c` main 函式結束的部分可以發現有 `add_quit_helper(q_quit)` 和 `finish_cmd` 兩個看起來跟程式結束有關的函式,進一步追蹤可以知道 `q_quit` 中第一行就 `return true;`
明顯有問題,刪掉之後就不會再報錯了。
除此之外,也可以發現 `qtest` 可以用 `add_quit_helper` , 以函式指標指向輸入的函式(例如: `q_quit` ) 存於 `quit_helpers` 陣列中,之後執行 `quit` 命令或是程式結束時, `do_quit()` 會呼叫陣列中指向的函式,如此一來就可以模組化 quit 時需要執行的函式。
### Linux 核心 Linked List 排序研究
:::info
TODO
:::
### 在 qtest 提供新的命令 shuffle