---
tags: linux2023-homework1
---
> [Linux 核心設計/實作 (Spring 2023)](http://wiki.csie.ncku.edu.tw/linux/schedule)
## 作業要求
> [2023q1 Homework1 (作業區)](https://hackmd.io/@sysprog/linux2023-homework1)
> [L01: lab0](https://hackmd.io/@sysprog/linux2023-lab0/)
- [ ] 在 GitHub 上 fork lab0-c
- [ ] 著手修改 `queue.[ch]` 和連帶的檔案,測試後用 Git 管理各項修改,要能滿足 `$ make test` 自動評分系統的所有項目。
- [ ] 研讀 Linux 核心原始程式碼的 [lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) 並嘗試引入到 `lab0-c` 專案,比較你自己實作的 merge sort 和 Linux 核心程式碼之間效能落差
- [ ] 確保 `qtest` 提供的 `web` 命令得以搭配上述佇列實作使用,目前一旦網頁伺服器啟動後,原本處理命令列操作的 `linenoise` 套件就無法使用,請提出改善機制
- [ ] 在 `qtest` 提供新的命令 `shuffle`,允許藉由 [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) 演算法,對佇列中所有節點進行洗牌 (shuffle) 操作,需要以統計的原理來分析你的實作,探究洗牌的「亂度」
- [ ] 在 `qtest` 中執行 `option entropy 1` 並搭配 `ih RAND 10` 一類的命令,觀察亂數字串的分布,並運用「資訊與熵互補,資訊就是負熵」的觀念,設計一組新的命令或選項,得以在 `qtest` 切換不同的 PRNG 的實作 (可選定 [Xorshift](https://en.wikipedia.org/wiki/Xorshift)),搭配統計分析工具,比較亂數產生器 (如 Xorshift vs. 內建) 的品質
- [ ] 研讀論文〈[Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf)〉,解釋本程式的 "simulation" 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 [Student's t-distribution](https://en.wikipedia.org/wiki/Student%27s_t-distribution) 及程式實作的原理。
- [ ] 指出現有程式的缺陷或者可改進之處 (例如更全面地使用 Linux 核心風格的鏈結串列 API),嘗試實作並提交 pull request
## 開發環境
```
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
```
```
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz
CPU family: 6
Model: 60
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
Stepping: 3
CPU max MHz: 3400.0000
CPU min MHz: 800.0000
BogoMIPS: 6385.16
Caches (sum of all):
L1d: 128 KiB (4 instances)
L1i: 128 KiB (4 instances)
L2: 1 MiB (4 instances)
L3: 6 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-3
```
## 開發進度 (53/100)
- [x] `q_new`
- [x] `q_free`
- [x] `q_insert_head`
- [x] `q_insert_tail`
- [x] `q_remove_head`
- [x] `q_remove_tail`
- [x] `q_size`
## 開發過程
### q_new
使用 `malloc` 函式進行記憶體配置,若配置記憶體空間失敗則回傳 `NULL` ,使用 `queue.h` 及連帶的 `list.h` 中所提供的巨集與函式。以 `INIT_LIST_HEAD()` 取代將 `next` 和 `prev` 都指向自身 (`head`) 的初始化實作。
```c=
struct list_head *q_new()
{
struct list_head *new = malloc(sizeof(struct list_head));
if (!new)
return NULL;
INIT_LIST_HEAD(new);
return new;
}
```
### q_free
最初以 `list.h` 中提供的 `list_for_each_safe()` 來走訪節點,並以`q_release_element()` 來釋放走訪到的節點,但在編譯時卻產生出 **error: passing argument 1 of ‘q_release_element’ from incompatible pointer type** 。經檢查後發現 `q_release_element()` 中的 **expected type** 應該要為`element_t *` ,且 `struct element_t` 才有包含 `char` 與 `*value` 的變數宣告。
改進方案: 改以 `element_t *` 宣告 `entry` 和 `safe` ,並且以`list_for_each_entry_safe()` 取代 ~~`list_for_each_safe()`~~ 。
```c=
void q_free(struct list_head *l)
{
if (!l)
return;
element_t *cur, *safe;
list_for_each_entry_safe (cur, safe, l, list) {
q_release_element(cur);
}
free(l);
}
```
### q_insert_head
確認 `head` 是否為空,以及確認 `ele` 記憶體配置是否成功後,宣告新增字串的長度 (包含`\0`) 並串接字串 `s` 。使用 `list.h` 中提供的 `list_add()` 將新增的節點加入至串列的 `head` 。
```c=
bool q_insert_head(struct list_head *head, char *s)
{
element_t *ele = malloc(sizeof(element_t));
if (!head || !ele)
return false;
ele->value = malloc((strlen(s) + 1) * sizeof(char));
if (!ele->value) {
free(ele);
return false;
}
strncpy(ele->value, s, strlen(s) + 1);
list_add(&ele->list, head);
return true;
}
```
### q_insert_tail
以 `q_insert_head()` 為基礎,僅將 `list.h` 中提供的 `list_add()` 改為 `list_add_tail()` 。
```c=
bool q_insert_tail(struct list_head *head, char *s)
{
element_t *ele = malloc(sizeof(element_t));
if (!head || !ele)
return false;
ele->value = malloc((strlen(s) + 1) * sizeof(char));
if (!ele->value) {
free(ele);
return false;
}
strncpy(ele->value, s, strlen(s) + 1);
list_add_tail(&ele->list, head);
return true;
}
```
### q_remove_head
使用 `list_first_entry()` 取得 queue 中的第一個節點後,以 `list_del_init()` 來移除找到的第一個節點,並將移除之節點中所包含的字串存於 `sp` ,最後再回傳這個被移除的節點。
```c=
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *ele = list_first_entry(head, element_t, list);
list_del_init(head->next);
if (ele && bufsize) {
strncpy(sp, ele->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return ele;
}
```
### q_remove_tail
以 `q_remove_head()` 為基礎,僅將 `list.h` 中提供的 `list_first_entry()` 改為 `list_last_entry()` ,即為找到並取得 queue 中的最後一個節點。
```c=
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *ele = list_last_entry(head, element_t, list);
list_del_init(head->next);
if (ele && bufsize) {
strncpy(sp, ele->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return ele;
}
```
### q_size
使用 `list_for_each()` 來走訪所有的節點,並參照 [L01: lab0](https://hackmd.io/@sysprog/linux2023-lab0/%2F%40sysprog%2Flinux2023-lab0-a) 來實作此介面。
```c=
int q_size(struct list_head *head)
{
if (!head)
return 0;
int len = 0;
struct list_head *li;
list_for_each (li, head)
len++;
return len;
}
```
### q_delete_mid
參考 [你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list) ,並以 [Leetcode 2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/) 案例,應用快慢指標的技巧,並刪除找到的中間節點。
```c=
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
struct list_head *cur = head->next;
struct list_head *fast = head->next;
while (fast != head && fast->next != head) {
cur = cur->next;
fast = fast->next->next;
}
list_del(cur);
q_release_element(list_entry(cur, element_t, list));
return true;
}
```
### q_delete_dup
走訪每個節點,並檢查每個節點往後至尾端的最後一個節點,檢查節點之中是否存在相同的字串。
```c=
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
struct list_head *cur = head->next, *del;
while (cur->next != head) {
if (strcmp(list_entry(cur, element_t, list)->value,
list_entry(cur->next, element_t, list)->value) == 0) {
del = cur->next;
list_del(del);
q_release_element(list_entry(del, element_t, list));
} else {
cur = cur->next;
}
}
return true;
}
```
## 預期進度