contributed by < rain20010126
>
詳閱作業說明,指定的格式中有空白字元,留意各式細節,唯有重視小處並步步為營,方可挑戰原始程式碼規模超越三千七百萬行的 Linux 核心
了解,感謝老師指正!
Shawn531
queue.h
有說明每個函式要達成甚麼目的,這樣可以加速開發。diff
表達程式碼的差異。ShawnXuanc
在提交 commit
時可以增加對功能的說明
可以將相同的程式碼再利用,像是 q_insert
, q_remove
中有需多重複的程式碼
可以使用美式英文當作程式碼的註解
注意標示的方法。
-o
: 指定特定輸出的檔案名稱
-g
: 可以配合 gdb
做除錯如$ gdb -q stack
,其中-q
的目的為讓他安靜一點
-no-pie
: 抑制 PIE,簡單來說就是將原本執行檔是把一段位置寫進去(直接的地址),使用 PIE 的話就是改成相對位置,來避免針對內存位置的攻擊,所以使用 PIE 後需要對執行檔做追蹤的話會比較麻煩
我在調整風格時遇到以下問題,有嘗試做 sudo apt upgrade gdb
仍然無法解決此問題
另外的問題是我在使用 disas main
的輸出的結果如下, 不知道要怎麼和老師在 你所不知道的 C 語言:函式呼叫篇 中的 rbp
和 rsp
做對應
malloc
盡可能用已經用過並且釋放掉的記憶體空間; calloc
則是配置記憶體空間並將內容歸零(通常會使用沒有被使用過的記憶體空間或是 calloc
過後但沒有做進一步修改的記憶體空間),如果要配置歸零後的記憶體空間使用 malloc
,相比於使用 malloc + memset
的速度來的快,如果沒有歸零的需求,先使用 malloc
,因為 malloc
的記憶體配置成功率較高
Parameter
為形式, Argument
為實際值
概念為後入先出,使用區域變數會配置在 stack
中。
stack frame
為一層的區域
概念為分堆,會分成不同大小,例如由小到大為 first bit
small bit
big bit
等等,會有不同的配置策略。
使用 malloc
, free
等配置記憶體或是釋放記憶體都有關係,以下為 heap
和 stack
的範例
遞迴使用 function,在 function 中呼叫相同的一個 function (直接遞迴),或是 function A
會呼叫 function B
,而 function B
會呼叫 function A
(間接遞迴)
遞迴與非遞迴比較
&&
(Logical AND) operator 與 ||
(Logical OR) operator
無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利)
在安裝完 Ubuntu Linux,熟悉鏈結串列和 git 的操作後我開始嘗試寫作業,順序即是每個函式的順序,因為一開始不知道怎麼下手,就先參考其他同學的格式並思考如何下手,首先看到 Jackiempty q_new
的部份後,發現我也不知道有 INIT_LIST_HEAD(list_head)
的函式存在,以及同學其他呼叫的函式都不太熟悉,在了解我到不是很熟悉下列網站後 (https://github.com/torvalds/linux/blob/master/include/linux/list.h#L35) ,所以我寫作業的方法主要是先參考同學的寫法,了解大概有什麼函式是需要用的,理解完需要用到的函式後,再自己下手。
q_new
INIT_LIST_HEAD
即順利完成此函式的建立。q_free
你不該花太多時間在「查找資料」,閱讀、思考,然後闡述自己的洞見,才是課程在意的議題。
好的,謝謝老師。
在理解 list_for_each_entry_safe
時遇到了不小的困難,後來在查找資料 時,發現我的理解大有問題,首先 #define
的意思是一個巨集指令,和一般定義一個函式的方式有些不同,我原先以為他的意思和定義函數是一樣的意思,再來參照 aa860630 及 nelson0720j,將巨集的輸入設定成 entry
和 safe
後,了解到此巨集主要的作用是在刪除當前節點時,能同時知道下一個節點的位置,有這樣的理解後,完成了 q_free
的建立。
q_insert_head
本該如此,有何好說?
自己嘗試!
這次我是自己從頭嘗試看看,但配置新的節點後,我遇到了一個問題是該怎麼知道配置 node->value
的記憶體空間,於是我詢問 chatgpt 來嘗試自己解決問題 ,結果他給我一段範例程式碼,所以在我閱讀後,了解到可以使用 strlen
來知道字串的長度,同時加 1 是為了存儲字符串結束符 (\0),以及需要考慮到記憶體配置失敗的問題,下一個問題是我要怎麼把輸入的字串複製過去 ,之後在閱讀 nelson0720j 的說明後了解到可以使用 strncpy
,於是以下是我的初步程式碼。
注意詞彙:
接著我使用 make test
進行測試後發現函式有誤,同時有內存洩漏的問題,於是我參考了 csotaku0926 的作法,檢查我出錯的地方,發現我少考慮到了單個字體的大小,以及在 return false
前應該要把配置失敗的記憶體釋放掉,接著就成功了~~~
q_insert_tail
避免非必要的項目縮排 (即 *
和 -
),以清晰、明確,且流暢的漢語書寫。
有了 q_insert_head
的經驗,於是我就將 q_insert_head
的最後一行程式碼從 list_add(&node->list, head);
修改成 list_add_tail(&node->list, head);
。
另外有看到 csotaku0926 的作法,覺得他的作法聰明許多,他是直接利用先前寫的 q_insert_head
來完成函式,即將函式輸入部份從原先的 *head
改成 head->prev
就完成了,這種作法就可以省去重複的程式碼。
q_remove_head
在一開始,我初步對這個函式的理解是'移除' head
中和 *sp
指標指向相同的元素開始後的 bufsize
個元素,因為不確定我的想法是否正確,所以我查看了 csotaku0926 的程式碼,發現我的想法大錯特錯,了解到 bufsize
是用來存取 head->value
的大小的,同時 sp
是用來儲存 head->value
的原先值的,並搭配 list_del_init
斷開head的連結,因此在有這些了解後完成了程式碼。
我比較疑惑的部份是為什麼 remove
需要刪除節點內的 value
另外一開始在撰寫程式時沒注意到 queue empty 的情況
q_remove_tail
因為有先前 q_insert_head
和 q_insert_tail
的經驗,所以也嘗試看能不能用 q_remove_head
的方式撰寫本函式,但是失敗了,於是我參考了 csotaku0926 的程式碼,發現是要使用 head->prev->prev
但依然遇到了以下問題
不理解就說不理解,不要裝可憐。理工人員說要精準。
已解決,將 list_del_init
修改成 list_del
,但是有點不太了解其中原因,有查看 你所不知道的 C 語言: linked list 和非連續記憶體 的說明,但不懂兩者的差別為何會造成一個編譯成功一個編譯失敗的問題
*
),使用清晰、明確,且流暢的漢語書寫q_size
參考自 牛刀小試 ,透過 list_for_each
走訪每個節點,同時經過一個節點時 len
加一
q_delete_mid
在閱讀完你所不知道的 C 語言: linked list 和非連續記憶體後,我有一個初步的了解是可以使用快慢指標來找到中間的節點,以下是我第一次完成的程式碼
但是遇到了以下問題
後來在參考 nosba0957 ,發現迴圈結束後不需要再做一次 slow = slow-> next;
,但修改後仍然出現同的問題,最後在解決前面 q_remove_tail
的問題後就沒有出現上述問題了
另外在 git commit
時收到了將 tail
設定成常數的建議
HackMD 不是讓你張貼完整程式碼的地方,GitHub 才是!因此你在開發紀錄只該列出關鍵程式碼 (善用 diff 標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」,不是用來「假裝自己有付出」
了解函式的目標是在已經排序的狀況,移走佇列中具備重複內容的節點,我初步的想法是重複的元素會相鄰,我就從頭一個一個檢查看節點的內容,ㄖ,並了解可以使用 strcmp
,若是兩者字串相等的話會回傳0,並不使用 node = node->next
以便再重新檢查和下一個元素是否也出現重複得情況,若兩字串不相等,則使用 node = node->next
,對下一個節點進行檢查,以下是我初步的程式碼
接著我在使用 git commit
後收到了針對 current 變數的警告,要在使用這個變數的區域內聲明它,以減少其作用範圍,增加程式碼的可讀性和維護性。
另外在 q_test
出現了以下問題
接著我查看了 csotaku0926 後發現我沒有正確理解題目的意思,重複的每個節點都需要刪除,所以我新增了以下程式碼如下,同時使用 remove_cur
判斷是否有刪除節點 ,但是在 q_test
依然出現了相同的問題。
因為找不到問題所在,同時我發現我先前的程式碼沒有考慮到有重複多次的情形,所以我將迴圈改成 while
的寫法,以下是我的最終版本,我參考了 csotaku0926 的作法,改進了我先前沒有繼續檢查的問題。
再來我在執行 qtest
再有多目標需要刪除時會出現以下問題,經過了解後,我發現需要在以上 while 迴圈有誤處考慮 next
為 null 的情況,因次在條件部份補上 && next
即完成程式碼
避免非必要的項目縮排 (即 *
),以清晰、明確,且流暢的漢語書寫。
q_swap
一開始我沒有很理解題目的意思,但在查看完 nosba0957 的文字說明後,就了解題目是兩兩節點進行交換,交換完後再到下兩個節點進行交換,並參考該同學的程式碼,使用 list_move
對節點進行移除再插入。
q_reverse
我的初步想法是把 head->next
也就是 first
插入到 tail
後,再重新設定 first
,以下是我初步的程式碼。
但出現以下錯誤
後來發現我條件部份沒設定好,會出現無窮迴圈的情況,將條件重新設定成 while(tail->prev!=head)
即完成函式的建立。
q_reverseK
我的想法是把tail設定到需要反轉部份的尾端,然後再用先前 q_reverse
的方法完成,以下為我的程式碼。
後來同學在查看我的程式碼後指出我理解錯題目的要求了,題目要求是將 k 個為一組的元素進行反轉,所以我使用了 size
來判別剩下的元素還有沒有 k 個,沒有的話即跳出迴圈,接著利用前面 reverse
的方法,不斷更新 head
和 tail
,並利用 this_term_head
來存取此次循環的 head
作為 while 迴圈判斷使用。
q_sort
在觀看 Linked List Sort ,並了解不同作法的原理後,我選擇以時間複雜度較低的方法: Merge Sort
,也就是先找到佇列的中點,慢慢拆分到每個佇列只包含一個元素,再兩兩合併成完整的一個佇列,於是我開始實做。
在實做過程中,首先我遇到的困難是我該如何儲存每個斷開節點的位置,於是我查看了 WangHanChi 的程式碼和說明
但實做的第一次結果遇到了以下問題,所以我慢慢針對不同的函式下去做修正
以下為我 merge_two_nodes
的程式碼
什麼是「虛擬」的節點?
後來發現我回傳的數值有問題,需要再額外存取 head
的位置再做回傳,後來我了解到可以使用虛擬節點 ??? (改正:可以使用額外的一個 h 來初始化 head,這樣可以讓 head 在迴圈外部始終指向正確的位置,也不用特別處理第一個元素) 方式更好的解決此問題,也就是以下
程式碼的註解都該用美式英語書寫,不要出現漢字。
你如何確保測試的過程已涵蓋排序演算法的最差狀況?
q_descend
了解到題目是要移除當該節點右側有大於該節點的節點,則刪除該節點,初步想法就是按照順序一個一個檢查,但想一想覺得這個方法會重複確認很多次,於是我就參考了 csotaku0926 的說明,了解到可以從尾端往前檢查,看有沒有比自己小的節點做刪除。
清楚標註你參照的 GitHub 帳號名稱
另外看到函式需要回傳一個數值,查看同學 csotaku0926的程式碼了解是要回傳佇列的節點數量,於是完成我以下的程式碼
q_merge
我採用了比較直觀的做法,同樣先把環形串列修改成非環形的,並將第一個串列使用 merge_two_nodes
不斷與後續的進行連接,最後與 q_sort
使用相同的作法將串列修改回雙向環狀的
目前使用 make valgrind
在程式碼中沒有找出記憶體的相關問題,但使用 make test
時無法通過第17項的測試
此方法是透過一個迴圈去迭代,從最後一個元素開始,與前面的隨機一個元素進行交換,此作法的好處是,可以確保交換過得位置不會再被交換,且時間複雜度為 O(n) ,機率分佈也是均勻的
新增的方法參考 willwillhi ,目前 qtest
中的 do_shuffle
沒有檢查可能的錯誤
在實作 q_shuffle
時,遇到了以下問題
在檢查完程式碼後,發現第 16 行部份有誤,在與前面交換後,原本 temp
所在的位置變為 pick
所在,因此往前更新應該修改為 temp = pick->prev
但在進行腳本測試時的結果如下
於是重新利用 qtest
進行檢查,發現在執行第二次 shuffle 時會出現以下問題
接著我比較了自己的程式碼與 willwillhi 的差別,發現我的 swap
函式需要考慮當交換元素是相鄰的情況,只需做一次 list_del
與 list_add
即可,最後我的 q_shuffle
的結果如下
Entropy 定義是某個隨機事件平均每個結果發生時所能傳達出的資訊量,公式如下
其中
為期望值,由上式可知,當一個事件的機率越低,所獲得的資訊量則越多
面對時序攻擊,舉例來說就是根據密碼的執行時間來推斷密碼,這時判斷執行時間是否是常數時間就十分重,因此本篇論文開發出一種工具 dudect
,一個透過統計分析的程式判斷程式是否是以常數時間執行,解決了底層硬體的問題,也就是無法獲得 CPU 運作訊息的問題
dudect
所使用的方法是 timing leakeage detection tests
,首先測量兩種不同輸入數據類型的執行時間,然後檢查這兩種時間分佈是否在統計上有顯著差異,運用 fix-vs-random 產生兩種測試資料,第一種資料是常數,第二種資料是亂數,而第一種資料通常會給定一個較特別的值,來了解極端狀況
在統計分析前會先進行後處理如 Cropping: 從一組測量值中刪除或捨棄那些偏離正常值或超出 threshold 的測量值,減少不符合正常情況數據的影響,或是其他高階處理
接著是應用統計測試來嘗試反駁 null hypothesis: the two timing distributions are equal
,若是成功反駁此假說,則說明執行時間非常數時間,在本論文中使用 Welch’s t-test 來檢測兩個分佈是否相同
以下為 Welch’s t-test 在本文應用的說明
在進行 Welch’s t-test 時, 越接近 0 代表兩組數據的差異越小,在論文中 的 threshold 為 4.5 , t 的定義如下
在作業要求中有提到在 oreparaz/dudect 的程式碼具備 percentile 的處理,但在 lab0-c 則無,因此實作目標為在 lab0-c/dudect
中新增此項功能,根據 dudect 可以了解 percentile
的作用為設定閾值,也就是先前所說的後處理的技巧 Cropping