# 2022q1 Homework1 (lab0)
contributed by < `BensonYang1999` >
###### tags: `linux2022`
## 實驗環境
```shell
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ uname -r
5.13.0-28-generic
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
Address sizes: 39 bits physical, 48 bits virtual
CPU(s): 12
On-line CPU(s) list: 0-11
Thread(s) per core: 2
Core(s) per socket: 6
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 158
Model name: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
Stepping: 10
CPU MHz: 1570.377
CPU max MHz: 5000.0000
CPU min MHz: 800.0000
BogoMIPS: 7399.70
Virtualization: VT-x
L1d cache: 192 KiB
L1i cache: 192 KiB
L2 cache: 1.5 MiB
L3 cache: 12 MiB
NUMA node0 CPU(s): 0-11
```
## 作業要求
> [lab0](https://hackmd.io/@sysprog/linux2022-lab0)
依據指示著手修改 queue.[ch] 和連帶的檔案,測試後用 Git 管理各項修改。
* q_new: 創一個空的 queue
* q_free: 釋放掉一個 queue
* q_insert_head: 在 head 插入一個 element (LIFO)
* q_insert_tail: 在 tail 插入一個 element (FIFO), O(1)
* q_remove_head: 把 head 的 element 移除
* q_size: return queue 的大小
* q_reverse: 將 queue 反轉,不配置或釋放任何的 element
* q_sort: 將 queue 由小排到大, sort by nature sort
## 開發過程
### q_new
建立空的`list_head`,並利用`list.h`內建函數初始化
```c=
struct list_head *q_new()
{
struct list_head *q = malloc(sizeof(struct list_head));
// check memory allocation
if(!q)
return NULL;
// using INIT_LIST_HEAD function to initialize
INIT_LIST_HEAD(q);
return q;
}
```
### q_free
`list_for_each_entry_safe` 會將entry的下一個element存在safe當中,因此可以安全的移除entry,而不會造成list中斷(找不到下一個element)
```c=
void q_free(struct list_head *l)
{
// list is empty -> return
if(!l)
return;
element_t *entry, *safe;
//safely delete every elememt
list_for_each_entry_safe (entry, safe, l, list) {
q_release_element(entry);
}
}
```
### q_insert_head
1. 先確認head是否存在
2. 分配記憶體空間給新的節點
3. 將string複製到節點中
4. 將節點加到head的前面
```c=
bool q_insert_head(struct list_head *head, char *s)
{
if (!head)
return false;
// allocate memory for new node
element_t *node = malloc(sizeof(element_t));
if (!node)
return false;
// copy string
node->value = strdup(s);
list_add(&node->list, head);
return true;
}
```
測試時發現無法通過測資,判定需要考慮value未更改成功因此加入以下程式
```c
if (!node->value) {
q_release_element(node);
return false;
}
```
### q_insert_tail
與`q_insert_head`類似,只有把`list_add`改成`list_add_tail`
```c=
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head)
return false;
// allocate memory for new node
element_t *node = malloc(sizeof(element_t));
if (!node)
return false;
// copy string
node->value = strdup(s);
if (!node->value) {
q_release_element(node);
return false;
}
list_add_tail(&node->list, head);
return true;
}
```
### q_remove_head
1. 確認head是否存在及head是否是空的
2. 找到元素,並將其remove
3. 將元素的值複製到sp
```c=
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
// find target element
element_t *node = list_first_entry(head, element_t, list);
// remove
list_del_init(head->next);
if (sp) {
strncpy(sp, node->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return node;
}
```
### q_remove_tail
與`q_remove_head`類似,只有修改找元素的function
```c=
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
// find target element
element_t *node = list_last_entry(head, element_t, list);
// remove
list_del_init(head->prev);
if (sp) {
strncpy(sp, node->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
return node;
}
```
### q_size
直接利用範例的程式碼
```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
利用`q_size`取得佇列大小,計算中間值得位置,在利用迴圈找到要刪除的節點
```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;
int count = q_size(head) / 2;
struct list_head *node = head->next;
while (count--)
node = node->next;
list_del(node);
q_release_element(list_entry(node, element_t, list));
return true;
}
```
### q_delete_dup
利用雙層while迴圈,當找到相同的元素時刪除,無相同則繼續尋找下個元素。
```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 *node = head->next, *node_del;
while (node != head) {
while (strcmp(list_entry(node, element_t, list)->value, list_entry(node->next, element_t, list)->value) == 0 &&
node->next != head) {
node_del = node;
node = node->next;
list_del(node_del);
q_release_element(list_entry(node_del, element_t, list));
}
node = node->next;
}
return true;
}
```
上述版本執行時會產生`dereference a null pointer`,代表刪除錯誤的指標,因此更改第二層迴圈,利用`if else`做判斷
```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 *node = head->next, *node_del;
while (node->next != head) {
if (strcmp(list_entry(node, element_t, list)->value,
list_entry(node->next, element_t, list)->value) == 0) {
node_del = node->next;
list_del(node_del);
q_release_element(list_entry(node_del, element_t, list));
} else {
node = node->next;
}
}
return true;
}
```
### q_swap
利用基礎prev及next原則實做這題
```c=
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head || list_empty(head))
return false;
struct list_head *node = head->next;
struct list_head *node_next = node->next;
while (node != head && node->next != head) {
// swap node & node_next
node->prev->next = node_next;
node_next->next->prev = node;
node->next = node_next->next;
node_next->prev = node->prev;
node->prev = node_next;
node_next->next = node;
node = node->next;
node_next = node->next;
}
}
```
### q_reverse
修改每個`list_head`的prev & next
```c=
void q_reverse(struct list_head *head)
{
if (!head || list_empty(head))
return;
struct list_head *node = head->next, *temp;
while (node != head) {
temp = node->prev;
node->prev = node->next;
node->next = temp;
node = node->prev;
}
temp = head->prev;
head->prev = head->next;
head->next = temp;
}
```
### q_sort
參考 [你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list) 中的排序程式
執行sorting前必須將head斷開,避免產生無窮迴圈,執行後在將全部元素的prev修正,並補回head。
```c=
struct list_head *mergeTwoLists(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head, **node;
for (node = NULL; L1 && L2; *node = (*node)->next) {
node = (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) < 0)
? &L1
: &L2;
*ptr = *node;
ptr = &(*ptr)->next;
}
*ptr = (struct list_head *) ((uintptr_t) L1 | (uintptr_t) L2);
return head;
}
struct list_head *mergesort(struct list_head *head)
{
if (!head || !head->next)
return head;
struct list_head *slow = head;
for (struct list_head *fast = head->next; fast && fast->next;
fast = fast->next->next)
slow = slow->next;
struct list_head *mid = slow->next;
slow->next = NULL;
struct list_head *left = mergesort(head), *right = mergesort(mid);
return mergeTwoLists(left, right);
}
void q_sort(struct list_head *head)
{
if (!head || list_empty(head))
return;
// cut head link
head->prev->next = NULL;
head->next->prev = NULL;
// merge sort
struct list_head *sorted = mergesort(head->next);
// link head & fix prev
head->next = sorted;
sorted->prev = head;
struct list_head *temp = sorted;
while (temp->next) {
temp->next->prev = temp;
temp = temp->next;
}
head->prev = temp;
temp->next = head;
}
```
## 參考資料
* 格式:[共筆示範](https://hackmd.io/@cccccs100203/linux2020-lab0)
* 同學作業:[ccs100203](https://hackmd.io/@cccccs100203/linux2020-lab0), [laneser](https://hackmd.io/@laneser/linux2022_lab0), [jim12312321](https://hackmd.io/@MMz_Y0CPR-2VCQTxd4pFIA/linux2022-lab0)
* 其他:
* [作業說明](https://hackmd.io/@sysprog/linux2022-lab0)
* [C語言中的strdup()函式和其與strcpy()函式的區別](https://www.itread01.com/article/1440470244.html)
* [Linux 核心原始程式碼巨集: container_of](https://hackmd.io/@sysprog/linux-macro-containerof)