contributed by < blue76815 >
queue.[ch]
和連帶的檔案,測試後用 Git 管理各項修改,要能滿足 $ make test
自動評分系統的所有項目。
lab0-c
專案,比較你自己實作的 merge sort 和 Linux 核心程式碼之間效能落差qtest
提供新的命令 shuffle
,允許藉由 Fisher–Yates shuffle 演算法,對佇列中所有節點進行洗牌 (shuffle) 操作qtest
提供新的命令 web
,提供 web 伺服器功能,注意: web 伺服器運作過程中,qtest
仍可接受其他命令
qtest
執行過程中的錯誤
qtest
再於命令提示列輸入 help
命令,會使 開啟 Address Sanitizer 觸發錯誤,應予以排除qtest
實作的記憶體錯誤,並透過 Massif 視覺化 "simulation" 過程中的記憶體使用量,需要設計對應的實驗queue.c
實作q_new
: 建立新的「空」佇列;q_free
: 釋放佇列所佔用的記憶體;q_insert_head
: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則);q_insert_tail
: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則);q_remove_head
: 在佇列開頭 (head) 移去 (remove) 給定的節點;q_release_element
: 釋放特定節點的記憶體空間;q_size
: 計算目前佇列的節點總量;q_delete_mid
: 移走佇列中間的節點,詳見 LeetCode 2095. Delete the Middle Node of a Linked Listq_delete_dup
: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 LeetCode 82. Remove Duplicates from Sorted List IIq_swap
: 交換佇列中鄰近的節點,詳見 LeetCode 24. Swap Nodes in Pairsq_reverse
: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點;q_sort
: 以遞增順序來排序鏈結串列的節點,可參閱 Linked List Sort 得知實作手法;pointer to pointer 參考 Linked lists, pointer tricks and good taste 寫法
實做 queue.c
時參閱
此操作為建立新的「空」佇列,因此是建立 struct list_head head
頭端,而不是新建一個 element_t 節點
INIT_LIST_HEAD()
初始化head注意書寫規範:中英文間用一個半形空白字元區隔 :notes: jserv
已修正
list_for_each_entry_safe
尋訪 struct list_head
環狀佇列list_del()
只是將 node
從其原本的 linked list 連接移除q_release_element(node)
才能將節點記憶體釋放malloc
配置新節點node->value
list_add()
將指定的 node 插入到 linked list struct list_head head
的開頭data
資料時,得用 element_t
才能配置圖片來源:你所不知道的 C 語言: linked list 和非連續記憶體
strlen(s) + 1
是因為
Linux Programming Interface 查詢 strlen 有提到DESCRIPTION
The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0').
RETURN VALUE
The strlen() function returns the number of bytes in the string pointed to by s.
strlen()
計算字元個數到 null byte ('\0') 就停止,因此字數不包含 null byte ('\0')。
多加一格,是為了在字串結尾多填一格 NULL 值,
避免 strlen(const char str)
計算字串長度時,把字尾相鄰的記憶體殘值也列入字元個數
malloc
配置新節點node->value
list_add_tail(&node->list, head);
插入到尾端list_first_entry()
,取得 linked list 的開頭list_last_entry()
,取得 linked list 的結尾list_for_each_entry_safe
struct list_head
環狀佇列計次q_size(head)
取得佇列長度mid_head
mid_head
後,用 list_entry()
取得節點內容刪除參考金刀的算法小册子 0876-链表的中间结点 改成快慢兩種指標做 Floyd’s Cycle detection
參考 SmallHanley 同學的方法
head
是否為 NULL
,是則回傳 false
。last_same_node
判斷相鄰重複的節點,是否已經刪除到最後一個。list_for_each_safe (node, tmp, head)
走訪每個節點node
內是儲存某 element_t 節點的記憶體位址,得用 list_entry(node, element_t, list)
和 list_entry(node, element_t, list)
才能解析出 element_t 節點內的 value 成員value
是否相同,是則將 last_same_node
設為 true
,然後從串列中移除此節點並釋放相關空間。value
不同,而 last_same_node
依然是 true
,代表此節點是相鄰重複的節點中的最後一個,將 last_same_node
設回 false
並釋放相關空間。
例如 link-list 1==>1==>1==>2==>3
當刪除到剩 1==>2==>3 時,此 1 節點就是相鄰重複節點中的最後一個list_entry 介紹 内核双链表遍历:带 safe 和不带 safe 的区别
从两个接口的定义差别只看出,list_for_each_safe() 的定义多了一个参数 n,这个参数 n 用于缓存下一个节点,其余并没有什么逻辑上的差异。
所以寫 list_for_each_safe (node, tmp, head)
相當於用 node
節點 跑 for
迴圈
在官方 linux/list.h 中,發現有 list_swap()
可調用
調用 list_swap()
編譯時出現
發現我們 lab0
專案內的 list.h
中
沒有加入官方 linux/list.h 的 list_swap()
程式碼
於是我自己將官方 list_swap()
程式碼加在 lab0
專案內的 queue.c
中
再次編譯
queue.c
中還要再補上 官方 linux/list.h list_replace()
編譯成功
將 prev
與 next
兩個指標的內容交換(可藉由 tmp_node
暫存)
struct list_head
為環狀佇列,當節點走訪一圈後就會回到 head
位址,利用此特點判斷 while(node!=head)
是否走訪一圈回到head
圖片來源:你所不知道的 C 語言: linked list 和非連續記憶體
next_node=node->next
,備份好下個節點prev
與 next
兩個指標內儲存的地址交換(藉由 tmp_node
變數,暫存節點地址)head
節點的 prev
與 next
也要交換參閱
mergesort_list()
的詳細實做如下
qtest
實作的記憶體錯誤以我們 lab0 內附的 traces/trace-15-perf.cmd 測試項為例
step1.產生 massif 方法
$ valgrind --tool=massif ./qtest -f traces/trace-15-perf.cmd
生成一個 massif.out.50141 檔
step2.印出massif 檔
例如我生成 massif.out.50141 檔
$ ms_print massif.out.5014
valgrind massif內存分析 或到我的 ubuntu 20.4 的 ubuntu software
搜尋 Massif Visualizer
我下載中英文兩套界面版本的 Massif Visualizer