---
tags: linux2022
---
# 2022q1 Homework1 (lab0)
contributed by < `ioksengtan` >
## 實驗環境
```shell
$ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.5 (clang-1205.0.22.11)
Target: arm64-apple-darwin20.5.0
Thread model: posix
```
## [作業要求](https://hackmd.io/@sysprog/linux2022-lab0)
### Queue operation implementation
- [x] `q_new`: 建立新的「空」佇列
- [ ] error handling: old blocks are still allocated
- [x] `q_size`: 計算目前佇列的節點總量
- [x] `q_insert_head`: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則)
- [x] `q_insert_tail`: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則)
- [x] issue: corruption when attempting to free queue after insert node to tail.
- [ ] what's the difference between &(ele->list) and &ele->list
- [ ] what’s the difference between strdup and malloc+memset+strncpy ?
- [x] `q_remove_head`: 在佇列開頭 (head) 移去 (remove) 給定的節點
- [x] ERROR: Corruption detected in block with address 0x123f04140 when attempting to free it
- [x] `q_remove_tail`: 在佇列尾端 (tail) 移去 (remove) 給定的節點
- [x] `q_free`: 釋放佇列所佔用的記憶體
- [x] `q_reverse`: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點
- [x] `q_delete_mid`: 移走佇列中間的節點
- [x] LeetCode 2095. Delete the Middle Node of a Linked List
- [x] pseudo diagram
- [x] `q_delete_dup`: 在已經排序的狀況,移走佇列中具備重複內容的節點
- [x] LeetCode 82. Remove Duplicates from Sorted List II
- [x] pseudo diagram
- [ ] what if the list is not sorted
- [ ] `q_swap`: 交換佇列中鄰近的節點
- [ ] LeetCode 24. Swap Nodes in Pairs
- [ ] `q_sort`: 以==遞增順序==來排序鏈結串列的節點
### new command implementation
- [ ] shuffle
提供新的命令 shuffle,允許藉由 Fisher–Yates shuffle 演算法,對佇列中所有節點進行洗牌 (shuffle) 操作
- [ ] web
提供新的命令 web,提供 web 伺服器功能,注意: web 伺服器運作過程中,qtest 仍可接受其他命令
### Documentation in Devlog
- [ ] 開啟 Address Sanitizer,修正 qtest 執行過程中的錯誤
先執行 qtest 再於命令提示列輸入 help 命令,會使 開啟 Address Sanitizer 觸發錯誤,應予以排除
- [ ] 運用 Valgrind 排除 qtest 實作的記憶體錯誤,並透過 Massif 視覺化 “simulation” 過程中的記憶體使用量,需要設計對應的實驗
- [ ] 解釋 select 系統呼叫在本程式的使用方式,並分析 console.c 的實作,說明其中運用 CS:APP RIO 套件 的原理和考量點。可對照參考 CS:APP 第 10 章重點提示
- [ ] 研讀論文〈Dude, is my code constant time?〉,解釋本程式的 “simulation” 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 Student’s t-distribution 及程式實作的原理。注意:現有實作存在若干致命缺陷,請討論並提出解決方案;
:::danger
Improve your writing! It is supposed to be an official document.
:notes: jserv
:::
:::info
Thanks a lot jserv for feedback.
I will continue refining my documentation.
:notes: ioksengtan
:::
## Queue operation implementation
### q_size
list_for_each
```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_new
1. create a new element
2. return the pointer to the next list
```c
struct list_head *q_new()
{
element_t *q = malloc(sizeof(element_t));
if (q == NULL) {
return NULL;
}
INIT_LIST_HEAD(&q->list);
return &q->list;
}
```
```
cmd> new
Freeing old queue
l = NULL
ERROR: Freed queue, but 2 blocks are still allocated
l = []
```
reference: https://myao0730.blogspot.com/2016/12/linux.html
### q_insert_head
1. new an element
2. new_ele->next = head->next
3. head->next = new_ele
using provided list_add() in list.h
```c
/**
* list_add() - Add a list node to the beginning of the list
* @node: pointer to the new node
* @head: pointer to the head of the list
*/
static inline void list_add(struct list_head *node, struct list_head *head)
{
struct list_head *next = head->next;
next->prev = node;
node->next = next;
node->prev = head;
head->next = node;
}
```
```c
bool q_insert_head(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
ele->value = malloc(sizeof(s));
memset(ele->value, '\0', strlen(ele->value));
strncpy(ele->value, s, strlen(s));
list_add(&ele->list, head);
return true;
}
```
#### testing
```
cmd> new
l = []
cmd> ih test
l = [test]
cmd> ih test2
l = [test2 test]
```
### q_insert_tail
reuse list_add_tail in list.h
```c
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
ele->value = malloc(sizeof(s));
memset(ele->value, '\0', strlen(ele->value));
strncpy(ele->value, s, strlen(s));
list_add_tail(&ele->list, head);
return true;
}
```
#### issue:
```
cmd> new
l = []
cmd> it test
l = [test]
cmd> free
ERROR: Corruption detected in block with address 0x12ef04130 when attempting to free it
l = NULL
```
##### Observation on solution
Corruption detected:
```c=
ele->value = malloc(sizeof(s));
memset(ele->value, '\0', strlen(ele->value));
strncpy(ele->value, s, strlen(s));
```
No Corruption detection:
```c=
ele->value = strdup(s);
```
Revised implementation of q_insert_tail
```c
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
ele->value = strdup(s);
list_add_tail(&ele->list, head);
return true;
}
```
:::danger
You must explain your observations and reactions.
:notes: jserv
:::
:::info
Thanks alot jserv for feedback,
I am cooking those information then document it later.
:notes: ioksengtan
:::
Then there is no corruption
```
cmd> new
l = []
cmd> it test
l = [test]
cmd> rt
Removed test from queue
l = []
cmd> it test
l = [test]
cmd> free
l = NULL
```
#### discussion: what's the difference between strdup and malloc+memset+strncpy ?
#### discussion: what's the difference between following two lines?
(1) ```list_add_tail(&(ele->list), head);```
(2) ```list_add_tail(&ele->list, head);```
using (1), will fail the static analysis when git commit.
```
lab0-c git:(master) ✗ git commit -a
Following files need to be cleaned up:
queue.c
queue.c:118:5: error: Memory leak: ele [memleak]
return true;
^
```
#### testing
```
cmd> new
l = []
cmd> it test
l = [test]
cmd> it test2
l = [test test2]
cmd> it test3
l = [test test2 test3]
```
### q_remove_head
```c=
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
if (sp) {
element_t *e = list_first_entry(head, element_t, list);
size_t len = strlen(e->value);
len = (bufsize-1)>len?len:(bufsize-1);
memcpy(sp, e->value, len);
sp[len] = '\0';
list_del(&e->list);
return e;
}
return NULL;
}
```
#### testing
```
cmd> new
l = []
cmd> ih test
l = [test]
cmd> ih test2
l = [test2 test]
cmd> rh
Removed test2 from queue
l = [test]
```
### q_free
1. given the pointer of queue head
2. using provided q_release_element(element_t *e)
3. release all elements until end of queue
```c=
void q_free(struct list_head *l)
{
if (l == NULL) {
return;
}
struct list_head *node = l->next;
while (node != l) {
element_t *e = container_of(node, element_t, list);
node = node->next;
q_release_element(e);
}
// cppcheck-suppress nullPointer
element_t *head = container_of(l, element_t, list);
free(head);
}
```
Following implementation is wrongful, causing segmentation fault.
```c
if (!l)
return;
// iterate over the list entries and remove it
element_t *entry, *safe;
list_for_each_entry_safe (entry, safe, l, list)
q_release_element(entry);
free(l);
```
```
cmd> new
l = []
cmd> ih test
l = [test]
cmd> ih test2
l = [test2 test]
cmd> free
ERROR: Attempted to free unallocated block. Address = 0x15bf042b8
ERROR: Attempted to free unallocated or corrupted block. Address = 0x15bf042b8
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
```
### q_delete_mid
when number is even (N=4)
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node2"
"node2"->"node3"
"node3"->"node_tail"
"*front"->"node"[color=Red]
"*end"->node_tail[color=Red]
}
```
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node2"
"node2"->"node3"
"node3"->"node_tail"
"*front"->"node2"[color=Red]
"*end"->node3[color=Red]
}
```
if front->next = end, remove node3
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node2"
"node2"->"node_tail"
"*front"->"node2"[color=Red]
"*end"->node3[color=Red]
}
```
when number is odd (N=3)
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node2"
"node2"->"node_tail"
"*front"->"node"[color=Red]
"*end"->node_tail[color=Red]
}
```
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node2"
"node2"->"node_tail"
"*front"->"node2"[color=Red]
"*end"->node2[color=Red]
}
```
if front = end, remove node2
```graphviz
digraph {
rankdir=LR;
size="8,5"
node [shape = circle];
"head"->"node"
"node"->"node_tail"
"*front"->"node2"[color=Red]
"*end"->node2[color=Red]
}
```
```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 *front = head->next;
struct list_head *end = head->prev;
while (true) {
if (front == end || front->next == end){
list_del(front);
q_release_element(list_entry(front, element_t, list));
break;
}
front = front->next;
end = end->prev;
}
return true;
}
```
### q_delete_dup
### q_swap
### q_sort
worst case:
1. quick sort: time: O(N^2), spce: O(N)
2. merge sort: time: O(nlogn), spce: O(N)
看到一些同學實作quick sort有遇到效能的問題。
複習了sort 的時空複雜度,的確quick sort 的最差情境會是N^2。
merge sort原理:
1. 將原始的list 以二分法切成最小單位(N=2 or 1)的sorted list
2. 再兩兩一組合併回來 ([Leetcode 24 Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/))
### web