---
tags: linux2022
---
# 2022q1 Homework1 (lab0)
contributed by < [kaiweike](https://github.com/kaiweike/lab0-c) >
## 實驗環境
:::spoiler 剛開始的實驗環境
```shell
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ 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): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 61
Model name: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
Stepping: 4
CPU MHz: 1405.132
CPU max MHz: 3100.0000
CPU min MHz: 500.0000
BogoMIPS: 5399.87
Virtualization: VT-x
L1d cache: 64 KiB
L1i cache: 64 KiB
L2 cache: 512 KiB
L3 cache: 3 MiB
NUMA node0 CPU(s): 0-3
```
:::
上面的環境一直故障,後來換成這個實驗環境
```shell
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
Address sizes: 48 bits physical, 48 bits virtual
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: AuthenticAMD
CPU family: 21
Model: 16
Model name: AMD A8-4500M APU with Radeon(tm) HD Graphics
Stepping: 1
Frequency boost: enabled
CPU MHz: 1511.772
CPU max MHz: 1900.0000
CPU min MHz: 1400.0000
BogoMIPS: 3793.17
Virtualization: AMD-V
L1d cache: 32 KiB
L1i cache: 128 KiB
L2 cache: 4 MiB
NUMA node0 CPU(s): 0-3
```
## 開發紀錄
```shell
$ make test
CC queue.o
LD qtest
scripts/driver.py -c
--- Trace Points
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 5/5
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, remove_head, remove_tail, and delete_mid
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, swap, and sort
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, swap, and sort
--- trace-05-ops 6/6
+++ TESTING trace trace-06-ops:
# Test of insert_head, insert_tail, delete duplicate, and sort
--- trace-06-ops 6/6
+++ TESTING trace trace-07-string:
# Test of truncated strings
--- trace-07-string 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-robust:
# Test operations on NULL queue
--- trace-10-robust 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-malloc:
# Test of malloc failure on new
--- trace-13-malloc 6/6
+++ TESTING trace trace-14-perf:
# Test performance of insert_tail, reverse, and sort
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-14-perf 0/6
+++ TESTING trace trace-15-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of insert_tail
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if time complexity of q_insert_tail, q_insert_head, q_remove_tail, and q_remove_head is constant
Probably constant time
Probably constant time
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 94/100
make: *** [Makefile:56: test] Error 1
```
## 基礎概念
* [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html)
* [offsetof](https://man7.org/linux/man-pages/man3/offsetof.3.html) ([參考 你所不知道的 C 語言: linked list 和非連續記憶體-Linked list 在 Linux 核心原始程式碼-簡化的實作](https://hackmd.io/@sysprog/c-linked-list#簡化的實作))
`offsetof(type, member)`
=>可得到成員的位址減去 struct 的起始位址
* [container_of](https://github.com/torvalds/linux/blob/master/include/linux/container_of.h) ([參考 Linux 核心原始程式碼巨集: container_of](https://hackmd.io/@sysprog/linux-macro-containerof))
`#define container_of(ptr, type, member)`
=> 可得到 type 結構的 address
* [Common vulnerabilities guide for C programmers](https://security.web.cern.ch/recommendations/en/codetools/c.shtml)
**“ Most vulnerabilities in C are related to buffer overflows external link and string manipulation. ”**
* ~~gets()~~ -> `fgets()`
* ~~strcpy()~~ -> `strlcpy()` or `strncpy()`
* ~~strcat()~~ -> `strncat()`
* ~~strcmp()~~ -> `strncmp()`
* ~~sprintf()~~ -> `snprintf()`
* string formatting
-> always hardcode the format string. At least, never let it come directly from any user's input.
* file opening
## 實做佇列(queue.c)
:::danger
不要只列出原始程式碼,要提出你的想法/推測,並予以驗證。請把開發紀錄當作「資訊科技公司面試的逐字稿」來面對,你現在若能用越專業的說法,明日就能表現更好。
:notes: jserv
:::
### q_new
:::spoiler 最初的寫法
```c
// Create empty queue.
// Return NULL if could not allocate space.
struct list_head *q_new()
{
struct list_head *q = malloc(sizeof(struct list_head));
if (q != NULL)
q->next = q->prev = q;
return q;
}
```
:::
參考 [2020leon](https://hackmd.io/@6649/linux2022-lab0) ,並改寫為更簡潔的寫法:
1. 利用 `malloc` 分配記憶體空間。
2. 利用 `INIT_LIST_HEAD` 初始化 `struct list_head` 物件成員。
```c
struct list_head *q_new()
{
struct list_head *q = malloc(sizeof(struct list_head));
if (q)
INIT_LIST_HEAD(q);
return q;
}
```
### q_free
:::spoiler 這個寫法一直出錯
```c
void q_free(struct list_head *l)
{
element_t *entry, *safe;
if(!l || list_empty(l))
return;
list_for_each_entry_safe( entry, safe, l, list)
q_release_element(entry);
free(l);
}
```
:::
後來刪掉 if 判斷中的 `list_empty(l)` 就好了,
因為當 `list_empty() = !0` 時表示 list is empty,
但 empty 還是指向有效結構,開頭(head)的值為NULL([參考 K01: lab0](https://hackmd.io/@sysprog/linux2022-lab0#-改寫自-CMU-電腦系統概論的作業))。
```c
void q_free(struct list_head *l)
{
element_t *entry, *safe;
if(!l)
return;
list_for_each_entry_safe( entry, safe, l, list)
q_release_element(entry);
free(l);
}
```
### q_insert_head
::: spoiler 這個寫法一直出現錯誤
```c
// Attempt to insert element at head of queue.
// Return true if successful.
// Return false if q is NULL or could not allocate space.
// Argument s points to the string to be stored.
// The function must explicitly allocate space and copy the string into it.
bool q_insert_head(struct list_head *head, char *s)
{
element_t n;
n.value = s;
if(list_empty(head))
return false;
list_add(&n.list, head);
return true;
}
```
:::
參考 [freshLiver](https://hackmd.io/@freshLiver/2022q1-hw1#%E9%87%9D%E5%B0%8D%E4%BD%87%E5%88%97%E7%9A%84%E6%93%8D%E4%BD%9C) 的寫法後,
發現原來是少了 `malloc` 和 `memcpy` 這兩個步驟,沒有配置指定大小的記憶體空間。
另外,發現 `list_empty` != `null`,
“ 若你有空的紅包袋,裡面的東西是 empty;
若你連紅包袋都沒有,就可以說 NULL; ” ([參考 K01: lab0](https://hackmd.io/@sysprog/linux2022-lab0#-改寫自-CMU-電腦系統概論的作業))。
```c
bool q_insert_head(struct list_head *head, char *s)
{
if(!head)
return false;
element_t *n = malloc(sizeof(element_t));
if(!n)
return false;
n->value = malloc(strlen(s)+1);
if(!n->value)
return false;
memcpy(n->value, s, strlen(s));
n->value[strlen(s)] = '\0';
list_add(&n->list, head);
return true;
}
```
參考 [freshLiver](https://hackmd.io/@freshLiver/2022q1-hw1#%E9%87%9D%E5%B0%8D%E4%BD%87%E5%88%97%E7%9A%84%E6%93%8D%E4%BD%9C) 的寫法,
進一步使用 `strdup` 簡化 `malloc`、 `memcpy` 和 `value[strlen(s)] = '\0'` 的操作:
```c
bool q_insert_head(struct list_head *head, char *s)
{
if(!head)
return false;
element_t *n = malloc(sizeof(element_t));
if(!n)
return false;
n->value = strdup(s);
if (!n->value) {
free(n);
return false;
}
list_add(&n->list, head);
return true;
}
```
### 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;
element_t *n = malloc(sizeof(element_t));
if(!n)
return false;
n->value = strdup(s);
if (!n->value) {
free(n);
return false;
}
list_add_tail(&n->list, head);
return true;
}
```
### q_remove_head
```c
// Attempt to remove element from head of queue.
// Return target element.
// Return NULL if queue is NULL or empty.
// If sp is non-NULL and an element is removed, copy the removed string to *sp
// (up to a maximum of bufsize-1 characters, plus a null terminator.)
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if(!head || list_empty(head))
return NULL;
element_t *n = list_first_entry(head, element_t, list);
list_del(head->next);
if(sp){
strncpy(sp, n->value, bufsize-1);
sp[bufsize-1] = '\0';
}
return n;
}
```
### q_remove_tail
```c
// Attempt to remove element from tail of queue.
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if(!head || list_empty(head))
return NULL;
element_t *n = list_last_entry(head, element_t, list);
list_del(head->prev);
if(sp){
strncpy(sp, n->value, bufsize-1);
sp[bufsize-1] = '\0';
}
return n;
}
```
### q_delete_mid
::: spoiler 這樣的寫法有誤:
```c
// Delete the middle node in list.
// The middle node of a linked list of size n is the
// ⌊n / 2⌋th node from the start using 0-based indexing.
// If there're six element, the third member should be return.
// Return true if successful.
// Return false if list is NULL or empty.
bool q_delete_mid(struct list_head *head)
{
if (!head || list_empty(head))
return false;
struct list_head *front = head->next;
struct list_head *back = head->prev;
while (front != back && front->next != back)
{
front = head->next;
back = back->prev;
}
list_del(front);
return true;
}
```
:::
參考 [LJP-TW](https://hackmd.io/@LJP/SyRFuull9) 的寫法後修改為:
```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;
if (list_is_singular(head)){
element_t *element = q_remove_head(head, NULL, 0);
q_release_element(element);
return true;
}
struct list_head *twoStep = head->next;
struct list_head *oneStep = head->next;
while (twoStep != head && twoStep->next != head){
twoStep = twoStep->next->next;
oneStep = oneStep->next;
}
element_t *element = list_entry(oneStep, element_t, list);
list_del(oneStep);
q_release_element(element);
return true;
}
```
### q_delete_dup
參考 [eric88525](https://hackmd.io/@eric88525/linux2022-lab0) 的寫法
```c
// Delete all nodes that have duplicate string,
// leaving only distinct strings from the original list.
// Return true if successful.
// Return false if list is NULL.
//
// Note: this function always be called after sorting, in other words,
// list is guaranteed to be sorted in ascending order.
bool q_delete_dup(struct list_head *head)
{
if(!head || list_empty(head))
return false;
struct list_head **p = &(head->next);
struct list_head *curr = head->next;
struct list_head *temp = NULL;
while(curr != head && curr->next != head){
if(strcmp(container_of(curr, element_t, list)->value,
container_of(curr->next, element_t, list)->value) == 0){
do{
temp = curr;
curr = curr->next;
list_del(tmp);
q_release_element(container_of(temp, element_t, list));
}while(curr->next != head &&
strcmp(container_of(curr, element_t, list)->value,
container_of(curr->next, element_t, list)->value) == 0);
}
p = &((*p)->next);
curr = curr->next;
}
return true;
}
```
### q_swap
```c
// Attempt to swap every two adjacent nodes.
void q_swap(struct list_head *head)
{
if(!head || list_empty(head))
return;
struct list_head *curr = head->next;
struct list_head *next = NULL;
while(curr && curr->next && curr != head && curr->next != head){
next = curr->next;
list_del(next);
list_add_tail(next, curr);
curr = curr->next;
}
}
```
### q_reverse
```c
// Reverse elements in queue
// No effect if q is NULL or empty
// This function should not allocate or free any list elements
// (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head).
// It should rearrange the existing ones.
void q_reverse(struct list_head *head)
{
if(!head|| list_empty(head))
return;
struct list_head *prev = head->prev;
struct list_head *curr head;
struct list_head *next NULL;
while(next != head){
next = curr->next;
curr->next = prev;
curr->prev = next;
prev = curr;
curr = next;
}
}
```
參考 [kdvnt](https://hackmd.io/WuqBNTdFQnuNxkFtR6FIWg?view#q_reverse) 更精簡的方法:
```c
void q_reverse(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
struct list_head *node, *safe;
list_for_each_safe (node, safe, head)
list_move(node, head);
}
```
### q_sort
[參考 你所不知道的 C 語言: linked list 和非連續記憶體- MergeSort的實作](https://hackmd.io/@sysprog/c-linked-list#Merge-Sort-的實作)
* 實做遞迴版本的 MergeSort:
```c
// Sort elements of queue in ascending order
// No effect if q is NULL or empty. In addition, if q has only one
// element, do nothing.
struct list_head *merge(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head;
while (L1 && L2) {
if (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) < 0) {
*ptr = L1;
L1 = L1->next;
} else {
*ptr = L2;
L2 = L2->next;
}
ptr = &(*ptr)->next;
}
*ptr = (struct list_head *) ((unsigned long int) L1 | (unsigned long int) L2);
return head;
}
struct list_head *mergesort(struct list_head *head)
{
if (!head || !head->next)
return head;
struct list_head *slow = head, *fast = head->next;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
fast = slow->next;
slow->next = NULL;
slow = head;
struct list_head *L1 = mergesort(slow);
struct list_head *L2 = mergesort(fast);
return merge(L1, L2);
}
void q_sort(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
head->prev->next = NULL;
head->next = mergesort(head->next);
struct list_head *prev = head, *cur = head->next;
for (; cur != NULL; prev = cur, cur = cur->next) {
cur->prev = prev;
}
prev->next = head;
head->prev = prev;
}
```
測項 14 和 15 指出效能不夠好
```bash
+++ TESTING trace trace-14-perf:
# Test performance of insert_tail, reverse, and sort
ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient
--- trace-14-perf 0/6
+++ TESTING trace trace-15-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-15-perf 6/6
```
推測可能是因為執行遞迴需要多次函式呼叫,如果呼叫層數比較深,增加的堆疊處理會影響效能。
* 實做迭代版本的 MergeSort:
```c
struct list_head *merge(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head;
while (L1 && L2) {
if (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) < 0) {
*ptr = L1;
L1 = L1->next;
} else {
*ptr = L2;
L2 = L2->next;
}
ptr = &(*ptr)->next;
}
*ptr = (struct list_head *) ((unsigned long int) L1 | (unsigned long int) L2);
return head;
}
struct list_head *mergesort_iter(struct list_head *head){
if (!head || !head->next)
return head;
int top = 0;
int listsSize = 0;
struct list_head *stack[1000] = {NULL};
struct list_head *lists[100000] = {NULL};
stack[top] = head;
while(top >= 0){
struct list_head *left = stack[top--];
if(left){
struct list_head *slow = left;
struct list_head *fast;
for(fast = left->next; fast && fast->next; fast = fast->next->next)
slow = slow->next;
struct list_head *right = slow->next;
slow->next = NULL;
stack[++top] = left;
stack[++top] = right;
}else{
lists[listsSize++] = stack[top--];
}
}
while(listsSize>1){
for(int i = 0, j = listsSize - 1; i < j; i++, j--)
lists[i] = merge(lists[i], lists[j]);
listsSize = (listsSize + 1) / 2;
}
head = lists[0];
return head;
}
void q_sort(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
head->prev->next = NULL;
head->next = mergesort_iter(head->next);
struct list_head *curr = head, *next = head->next;
while(next){
next->prev = curr;
curr = next;
next = next->next;
}
curr->next = head;
head->prev = curr;
}
```
Segmentation fault..改寫中
```bash
+++ TESTING trace trace-14-perf:
# Test performance of insert_tail, reverse, and sort
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-14-perf 0/6
+++ TESTING trace trace-15-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
```
## 實做洗牌(shuffle)
## 實做伺服器(web)
##
## 參考資訊
* [jim12312321](https://hackmd.io/@MMz_Y0CPR-2VCQTxd4pFIA/linux2022-lab0)
* [laneser](https://hackmd.io/@laneser/linux2022_lab0)
* [2020leon](https://hackmd.io/@6649/linux2022-lab0)
* [freshLiver](https://hackmd.io/@freshLiver/2022q1-hw1#%E9%87%9D%E5%B0%8D%E4%BD%87%E5%88%97%E7%9A%84%E6%93%8D%E4%BD%9C)
* [eric88525](https://hackmd.io/@eric88525/linux2022-lab0)
* [LJP-TW](https://hackmd.io/@LJP/SyRFuull9)
* [kdvnt](https://hackmd.io/WuqBNTdFQnuNxkFtR6FIWg?view#q_reverse)
<s>
linked_list
https://haogroot.com/2019/12/12/漫談-linked-list-在-linux-kernel-中的不一樣/
https://www.cntofu.com/book/46/linux_kernel/20.md
https://myao0730.blogspot.com/2016/12/linux.html
http://adrianhuang.blogspot.com/2007/10/linux-kernel-listhead.html
container_of
https://hackmd.io/@sysprog/linux-macro-containerof
http://adrianhuang.blogspot.com/2010/01/linux-kernel-containerof.html
https://myao0730.blogspot.com/2016/09/linuxcontainerof-offsetof.html
https://zhuanlan.zhihu.com/p/54932270
https://www.gushiciku.cn/pl/ggOa/zh-tw
https://blog.csdn.net/wukongmingjing/article/details/81746071
</s>
:::danger
不用花太多時間 Google 搜尋,大部分需要細讀的材料,我已經列在教材和作業說明中,請確保你閱讀「每一段落」及其指向的「每個」超連結。
:notes: jserv
:::