# 2022q1 Homework1 (lab0) ###### tags: `Linux 核心設計` `成大課程` contributed by < `hbr890627` > ## 實驗環境 暫時使用 WSL (Windows Subsystem for Linux) ``` $ 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): 12 On-line CPU(s) list: 0-11 Thread(s) per core: 2 Core(s) per socket: 6 Socket(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 151 Model name: 12th Gen Intel(R) Core(TM) i5-12400 Stepping: 5 CPU MHz: 2495.999 BogoMIPS: 4991.99 Virtualization: VT-x Hypervisor vendor: Microsoft Virtualization type: full L1d cache: 288 KiB L1i cache: 192 KiB L2 cache: 7.5 MiB L3 cache: 18 MiB ``` ## 進度 * 環境架設 * 暫時使用 WSL (Windows Subsystem for Linux) <s> * 2/20: 觀看[lab0 解說影片](https://www.youtube.com/watch?v=CMcbfQiQIHg) </s> :::warning 不需要在此標注日期,修改紀錄都在 HackMD 中。只是「觀看」不能算是成果,你至少該提出問題和摘要你所學到的事物。 :notes: jserv ::: * 嘗試修改 queue.c ## queue.c ### q_new ```c struct list_head *q_new() { element_t *e = malloc(sizeof(element_t)); if (!e) return NULL; e->value = NULL; INIT_LIST_HEAD(&e->list); return &e->list; } ``` ### q_free ```c void q_free(struct list_head *l) {} ``` ### q_insert_head ```C bool q_insert_head(struct list_head *head, char *s) { element_t *e = malloc(sizeof(element_t)); if (!e) return false; e->value = s; list_add(&e->list, head); return true; } ``` :::danger 注意書寫規範:中英文間用一個半形空白字元區隔。 :notes: jserv ::: 在 `make check` 後發現錯誤。 ```bash cmd> ih dolphin ERROR: Need to allocate and copy string for new queue element ``` 發現並未分配記憶體給 `e->value` ,修改程式碼。 ```c bool q_insert_head(struct list_head *head, char *s) { element_t *e = malloc(sizeof(element_t)); if (!e) return false; e->value = malloc(sizeof(s)); strcpy(e->value, s); list_add(&e->list, head); return true; } ``` 在 `make check` 時不再發生 ERROR ,但無法 git commit 。 ``` [2022-02-22T03:32:42.314Z] Following files need to be cleaned up: queue.c Dangerous function detected in /home/hbr/linux2022/lab0-c/queue.c 45: strcpy(e->value, s); Read 'Common vulnerabilities guide for C programmers' carefully. https://security.web.cern.ch/security/recommendations/en/codetools/c.shtml ``` 根據 https://security.web.cern.ch/recommendations/en/codetools/c.shtml ,將不安全的 `strcpy` ,改為 `strncpy` ,並補上字串的結尾`\0`。 ```c bool q_insert_head(struct list_head *head, char *s) { element_t *e = malloc(sizeof(element_t)); if (!e) return false; e->value = malloc(sizeof(char) * (strlen(s) + 1)); strncpy(e->value, s, strlen(s)); *(e->value + strlen(s)) = '\0'; list_add(&e->list, head); return true; } ``` 發現有些人使用 `strdup` 取代 `strcpy` ,雖然實作起來較方便,但這不包含在C的標準函式庫,不利於程式的移植性。 [參考資料](https://www.itread01.com/article/1440470244.html) ### q_insert_tail 大致上與 `q_insert_head` 差不多,將 `list_add` 改為 `list_add_tail` 。 ```c bool q_insert_tail(struct list_head *head, char *s) { element_t *e = malloc(sizeof(element_t)); if (!e) return false; e->value = malloc(sizeof(char) * (strlen(s) + 1)); strncpy(e->value, s, strlen(s)); *(e->value + strlen(s)) = '\0'; list_add(&e->list, head); return true; } ``` ### q_remove_head 要移除第一個節點,實際上就是要移除 `head->next` ,而在 `list.h` 中,有 `list_first_entry` 這個巨集可以使用,用來精簡程式碼。 ```c element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *node = list_first_entry(head, element_t, list); list_del(&node->list); if (sp) { strncpy(sp, node->value, bufsize); sp[bufsize - 1] = '\0'; } return node; } ``` ### q_remove_tail 與 `q_remove_head` 幾乎相同,將 `list_first_entry` 改為 `list_last_entry` 即可。 ```c element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *node = list_last_entry(head, element_t, list); list_del(&node->list); if (sp) { strncpy(sp, node->value, bufsize); sp[bufsize - 1] = '\0'; } return node; } ``` ### q_release_element 此函式會被外部程式使用到,不用修改即可。 ```c void q_release_element(element_t *e) { free(e->value); free(e); } ``` ### q_size 直接使用作業說明中的範例程式。 [作業說明-lab0-牛刀小試](https://hackmd.io/@sysprog/linux2022-lab0#%E7%89%9B%E5%88%80%E5%B0%8F%E8%A9%A6) ```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#%E4%BD%A0%E6%89%80%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84-C-%E8%AA%9E%E8%A8%80-linked-list-%E5%92%8C%E9%9D%9E%E9%80%A3%E7%BA%8C%E8%A8%98%E6%86%B6%E9%AB%94),使用龜兔賽跑的方式找到中間節點。 ```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 *slow = head->next, *fast = head->next; while (fast != head && fast->next != head) { slow = slow->next; fast = fast->next->next; } list_del(slow); q_release_element(list_entry(slow, 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) return false; element_t *entry, *safe; char *value = NULL; list_for_each_entry_safe (entry, safe, head, list) { if (strcmp(value, entry->value) == 0) { list_del(&entry->list); q_release_element(entry); } else { value = entry->value; } } return true; } ``` 此段程式碼會出現 `You dereferenced a NULL or invalid pointer` ,但一直找不到問題出現在哪,在參考 [mm0809](https://hackmd.io/@2QYhHFatQmWFUgzITEcRjA/B1lsUihy9#q_delete_dup) 後,發現只要使用`""`初始化 `value` 即可。 ```c bool q_delete_dup(struct list_head *head) { // https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/) if (!head) return false; element_t *entry, *safe; char *value = ""; list_for_each_entry_safe (entry, safe, head, list) { if (strcmp(value, entry->value) == 0) { list_del(&entry->list); q_release_element(entry); } else { value = entry->value; } } return true; } ``` ### q_swap ```c void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head || list_empty(head)) return; struct list_head *node = head->next; while (node != head && node->next != head) { struct list_head *next = node->next; list_del(node); list_add(node, next); node = node->next; } } ``` ### q_reverse 想要實作 reverse 的其中一個方式,就是從頭將結點移出,再照順序將結點 enqueue 即可,但根據作業要求,在此函式中,不能再額外分配記憶體用來暫時儲存暫時移出的節點,在思考解決方式時,參考 [mm0809](https://hackmd.io/@2QYhHFatQmWFUgzITEcRjA/B1lsUihy9#q_reverse) 的方式,只要先固定第一個指標後,不斷將後面的節點移到前面即可。 ```c void q_reverse(struct list_head *head) { if (!head || list_empty(head)) return; struct list_head *tmp = head->next; for (struct list_head *node = tmp->next; tmp->next != head; node = tmp->next) { list_del(node); list_add(node, head); } } ``` ### q_sort 使用到[你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list#%E4%BD%A0%E6%89%80%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84-C-%E8%AA%9E%E8%A8%80-linked-list-%E5%92%8C%E9%9D%9E%E9%80%A3%E7%BA%8C%E8%A8%98%E6%86%B6%E9%AB%94)中的 mergesort ,但是其實作的是單向的鏈結串列,必須在 merge 結束後,將 `prev` 的節點補上。 ```c /* * The sub-function of q_sort */ struct list_head *mergeTwoLists(struct list_head *h1, struct list_head *h2) { struct list_head *head = NULL, **ptr = &head, **node; for (node = NULL; h1 && h2; *node = (*node)->next) { node = strcmp(list_entry(h1, element_t, list)->value, list_entry(h2, element_t, list)->value) < 0 ? &h1 : &h2; *ptr = *node; ptr = &(*ptr)->next; } *ptr = (struct list_head *) ((uintptr_t) h1 | (uintptr_t) h2); return head; } /* * The sub-function of q_sort */ struct list_head *mergesort(struct list_head *head) { if (!head || !head->next) return head; // find middle node 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); } /* * 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. */ void q_sort(struct list_head *head) { if (!head || list_empty(head)) return; head->prev->next = NULL; head->next->prev = NULL; 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; } ```