contributed by < yanjiew1
>
以下複製自「作業要求」
queue.[ch]
和連帶的檔案,測試後用 Git 管理各項修改,要能滿足 $ make test
自動評分系統的所有項目。lab0-c
專案,比較你自己實作的 merge sort 和 Linux 核心程式碼之間效能落差
qtest
提供的 web
命令得以搭配上述佇列實作使用,目前一旦網頁伺服器啟動後,原本處理命令列操作的 linenoise
套件就無法使用,請提出改善機制qtest
提供新的命令 shuffle
,允許藉由 Fisher–Yates shuffle 演算法,對佇列中所有節點進行洗牌 (shuffle) 操作,需要以統計的原理來分析你的實作,探究洗牌的「亂度」qtest
中執行 option entropy 1
並搭配 ih RAND 10
一類的命令,觀察亂數字串的分布,並運用「資訊與熵互補,資訊就是負熵」的觀念,設計一組新的命令或選項,得以在 qtest
切換不同的 PRNG 的實作 (可選定 Xorshift),搭配統計分析工具,比較亂數產生器 (如 Xorshift vs. 內建) 的品質qtest
執行過程中的錯誤qtest
實作的記憶體錯誤,並透過 Massif 視覺化 "simulation" 過程中的記憶體使用量,需要設計對應的實驗queue.c
q_new
透過 malloc
來配置記憶體,使用 list.h
裡面提供的 INIT_LIST_HEAD
來初始化 struct list_head
。
commit 0f09bf6
q_free
使用 list.h
內提供的 list_for_each_entry_safe
來走訪每一個節點。使用的過程中,safe
會存放下一個節點,而 it
節點可以從 linked list 移除,不影響 list_for_each_entry_safe
運作。故在這裡,可以直接在走訪的過程中釋放每一個節點。最後再呼叫 free
釋放鏈結串列所配置的記憶體空間。。
一開始程式碼如下:
隨後很快發現,應該要釋放 element_t
才對,因為 struct list_head
是包含在 element_t
裡面的一個成員。於是變更如下:
但因為釋放 element_t
很常被用到,所以後來我寫一個獨立函式 q_free_elem
來做。最後又發現 queue.h
有提供 q_release_element
,最後改成用 q_release_element
,修改如下:
後來又仔細研究 list_for_each_entry_safe
巨集後,發現 list_del
可以不用做。此巨集的特性是我們可以把 it
直接釋放,只要不要動到 safe
,就可以安全操作。最後變更如下:
q_new_elem
配合 q_insert_head
和 q_insert_tail
要新增元素,新增此函式,用來建立 element_t
。
使用 malloc
來配置記憶體空間。使用 strdup
來配置字串空間並拷貝字串。過程中如果配置失敗,則回傳 NULL
。
commit 1e56bd7
q_insert_head
和 q_insert_tail
一開始要檢查 head
是否為 NULL
,若為 NULL
則不進行任何操作。
使用前面建立的 q_new_elem
來建立 element_t
。並且透過 list_add
或 list_add_tail
來把節點串上去。 若過程中失敗則回傳 false
。
q_copy_string
在 q_remove_head
和 q_remove_tail
需要複製字串,且 buffer 的大小是有限制的。C 標準函式庫提供的 strcpy
無法滿足此需求,故在此實作另一個函式 q_copy_string
來完成字串拷貝。
此函式是否必要?其通用的程度如何?
jserv
謝謝老師,已改用
strncpy
來處理
Commit 5b8c553
q_remove_head
和 q_remove_tail
這裡實作從佇列中移走開頭或尾端。
利用 list_first_entry
和 list_last_entry
來取得要移除的元素。使用 list_del
來移除元素。題目要求要把移除元素的字串值拷貝到 sp
這個 buffer ,並且 buffer 大小為 bufsize
,就透過前面實作的 q_copy_string
來做。
q_remove_tail
程式碼相似,就不佔用版面了。
正因程式碼相似度高,你更該思索如何簡化程式碼,從而降低維護的程式碼。
jserv
已讓
q_remove_tail
和q_insert_tail
重用q_remove_head
和q_insert_head
程式碼。謝謝老師
Commit bc97533
q_size
實作很簡單:直接使用 list.h
提供的 list_for_each
來走訪每一個節點。每走訪一個節點就遞增 count
變數,最後再回傳 count
變數的值即為所求。
q_delete_mid
這裡充份利用雙向鏈結串列的特性,從頭尾開始走訪節點,直到二個指標碰面時,即取得中間的節點。最後再把中間節點所代表的元素刪除。
q_delete_dup
我的作法是先宣告 cut
變數,其內容固定存放「目前走訪元素往前看,第一個其元素值與目前元素不同的元素」。
此外另外宣告一個 trash
pending
作為暫時放置待移除元素的地方,要移除的元素都會先放在這裡,最後再一起清掉。
"trash" 命名不精準,可用 pending list 一類的詞彙。
jserv
已修改。 Commit a725fcc
在走訪元素的過程中, &safe->list != head && strcmp(safe->value, it->value) == 0
&safe->list != head && !strcmp(safe->value, it->value)
會去判斷下一個元素跟目前元素的值是否相同。若相同時,就繼續走訪,直到遇到下個元素 safe
與目前元素 it
值不同時,再來進行接下來操作,如下:
當下個元素 safe
與目前元素 it
不同時,會先去檢查 cut
變數,是不是指向前一個元素。若是指向前一個元素,代表目前元素跟前一個元素不同,則不用處置,因為目前元素 it
跟前一個元素沒有重複;若 cut
不是指向前一個元素,則代表 cut
指向更之前的元素,且 元素其值均相同,故把 元素全部丟到 放到 trash
pending
中。
迴圈最後 cut = safe->list.prev;
這敘述,因為目前元素 it
與下一個元素值 safe
不同 (若相同就不會執行到此,故更新 cut
為目前元素後,再進行下個迴圈。
迴圈結束 q_delete_dup
要返回前。要清除 trash
pending
裡的垃圾元素。用 list_for_each_entry_safe
來走訪每一個 trash
pending
中的元素,並用 q_release_element
來刪除它。
將 strcmp(safe->value, it->value) == 0
改為 !strcmp(safe->value, it->value)
可縮減程式碼。
jserv
已修改 Commit bd9253a
q_reverse
這裡的實作很簡單,就是用 list_for_each_safe
走訪每一個節點。走訪過程中,用 list_move
把每一個節點移到開頭,這樣子順序就反過來了。 list_for_each_safe
允許對目前走訪的節點移動位置。因為是往前移到開頭,故不會因為節點移動而改變走訪次序或是重複走訪。
把 list_for_each_safe
大括號拿掉,按照 Coding Style ,這裡不用放大括號。
q_reverseK
使用 count
來記錄已走訪幾個節點,一開始把 count
設為 k
,每走訪一個節點就把 count
減去 1 。當走訪 k
個節點時 (--count
變成 0),就會把 cut
記錄的切點到目前走訪的節點(不包含 cut
但包含 it
,即 (cut, it]
)切下,放到 tmp
上,再重用前面實作好的 q_reverse
,對 tmp
做反轉,再用 list_splice
把 tmp
接回來。
最後設定 safe->prev
為新的 cut
。這裡不能用 it
是因為 it
已在反轉的過程中移動位置了。
追加 list_empty()
的檢查。
jserv
已修正
Commit b536d66
q_swap
swap
就是二個二個一組進行 reverse
,故直接重用 q_reverseK
的程式 。
q_merge_two
因為 q_sort
和 q_merge
都會需要合併二個串列。故新增一個 q_merge_two
用來合併二個串列。參考 alanjian85
同學的實作。合併的方式是每次從輸入的二個串列中,取較小的,放到一個暫存串列中 tmp
,之後再把串列接回 first
。
q_sort
採用 merge sort 遞迴演算法。一開始運用雙向鏈結串列的特性,從頭尾開始往中間找到中間節點,找到後,把 (head
, mid
] 留在 head
並把 (mid
, head
) 切下來加到 second
中。再針對 head
、 second
分別遞迴呼叫 q_sort
,對子串列進行排序。最後再把排序好的 head
和 second
透過 q_merge_two
合併。
若要改為迭代的實作,你預計怎麼做?
jserv
在還沒看到 Linux kernel 排序演算法前,我會傳統資料結構教科書內 bottom-up 方式實作。看到 Linux 排序演算法後,很明顯發現它比傳統教科書方式聰明。之後我會研究 Python 內的 Timsort 來實作。把 Timsort 實作出來後,新增一個
sort
option ,來切換排序演算法。
原本串列合併的程式直接寫在 q_sort
裡,後來把它移到獨立的函式中。
Commit 09de13c
q_descend
我的想法就是從尾到頭掃過一次。在掃的過程中,會判斷下一個節點(prev
)是否小於等於目前節點(cur
),若是就把下一個節點(prev
)刪除再重複迴圈,直到下一個節點是大於目前節點時,才會把 cur
移動到下一個節點,並且 cnt
(計數器) 加 1 。
目前 lab0-c
內建的 list.h
標頭檔和 Linux 核心原始程式碼 include/linux/list.h 存在一些差距,後者的 list_for_each_prev
巨集不在前者出現。若有它則可以用它來實作。
針對呼叫 strcmp
的那行敘述,若搭配使用 break
或 continue
關鍵字,可進一步縮減程式碼。
jserv
q_merge
採用 Strategies for Stable Merge Sorting 這篇論文中的 2-merge 演算法來實作。用此演算法,使合併二個 queue 時,其二個 queue 元素數量差距不要太大。
其運作原理是這樣:
x
, y
, z
。 若 |y| < 2|x|
則按下步驟進行合併,若否,則繼續進行 2. 迴圈
|x| < |z|
則將 x
和 y
合併,否則 y
和 z
合併x
、 y
、 z
,判斷是否符合需要合併的條件原本是這重用前面實作的 q_sort
。我把所有鏈結串列接在一起,然後呼叫 q_sort
排序好,放回第一個串列。後來變更實作,改成直接進行合併,並且合併時,透過一些策略降低二個 queue 要合併時的長度差異。
Commit f6568be
q_ascend
這是後來 lab-0
加上的函式,跟 q_descend
很像。故把 q_descend
的程式複製一份來用,把中間的 <
改為 >
即可。
make valgrind
的內容分析裡面看到很神奇的部份,就是在用 Valgrind 執行時,會把 qtest
執行檔中,所有 alarm
改為 isnan
。我的猜測是因為 Valgrind 會使程式執行速度變慢,導致一些操作會超過時間限制。我把這行拿掉 sed -i "s/alarm/isnan/g" $(patched_file)
,執行 make valgrind
,果然就出現超時訊息。查詢 C 標準函式庫可知,isnan(3p) 是用來檢查傳入的浮點數是否為 NaN ,會選用這個函數來取代 alarm
,估計是因為它跟 alarm
一樣函式名稱都是 5 個字元,此外 isnan
沒有任何 side effects ,故剛好可以拿來用。為了測試,我嘗試把 isnan
替換成 asinf
看看能不能運作。實測的結果如預期,可以正常運作。
執行 make valgrind
,產生了很多 Memory leak 的訊息。以下為節錄的訊息:
我先用 valgrind ./qtest
的方式測試,發現只要在結束前沒有把所有的 queue 釋放,就會產生上述訊息。後來發現在 q_quit
裡面,在第一行是 return true;
使下面釋放記憶體的程式沒辦法執行。把這行拿掉後就正常了。
但若是使用 valgrind ./qtest < traces/trace-01-ops.cmd
的方式來執行,仍然會出現下面訊息:
從上述訊息可以看出是 linenoise 中處理 history 的部份出錯。分析了一下 linenoise.c 的程式碼,發現 line_atexit
只會在 enable_raw_mode
函式裡被註冊為 atexit function ,但是在 stdin 不為 tty 的情況下 enable_raw_mode
不會被呼叫到。故把註冊 line_atexit
的程式移到 linenoise
function 就解決了。
至此,所有 make valgrind
會產生的記憶體問題都解決了。
重新編譯有開啟 address sanitizer 的版本。
之後執行 make test
,沒有出現任何記憶體相關錯誤訊息。至此檢查通過。
descend
lab-0
在 Commit 0fcefb0 加入了 descend
option ,故把上游版本 sysprog21/lab0-c
合併進來時,也需要一併實作 option descend
。
q_merge_two
因為 q_sort
和 q_merge
都會用到 q_merge_two
來合併二個串列。故在 q_merge_two
實作合併時,由大到小排序。其中迴圈內在選擇要哪一個元素放到合併後的串列時,做了些更動,如下:
而 q_merge_two
函式宣告改為:
針對 descend
為 true
的清況,正負號反轉,即可實現大到小排序,且也維持 q_sort
和 q_merge
的穩定排序特性。
q_sort
和 q_merge
而 q_sort
和 q_merge
則是修改成:呼叫 q_merge_two
時會多傳入 descend
參數,來決定是否要由大到小排序。
q_ksort
q_ksort
是使用 Linux 核心的 list_sort
排序演算法。list_sort
會傳入比較函式 cmp
作為參數。故我實作一個比較函式 q_cmp_descend
,它會呼叫原本的比較函式 q_cmp
,但正負號相反,如下:
再把 q_ksort
改成判斷 descend
是否為 true
,再選擇適當的比較函式。
list_sort
將 Linux 核心原始程式碼的 list_sort.c
和 list_sort.h
複製到 lab0-c
專案中,進行些許修改讓它可以成功在這個專案中編譯。主要是把 u8
改為 uint8_t
(並加入 stdint.h
) 、移除不必要的 #include
。之後修改 Makefile
加入 list_sort.o
後,確認用 make
命令可以正常編譯。
接下來是額外撰寫一份用 Linux kernel 排序的程式。因為 queue.h
不能更動,我決定把這個程式放在另一個檔案 ksort.c
並且有對應的標頭檔 ksort.h
。 此也在 Makefile
內加入 ksort.o
。
因為 Linux kernel 實作需傳入 cmp
比較函式作為比較大小用。我的實作如下:
我新增了一個 option ksort
,可以用來切換排序實作。在 qtest.c
按照原本定義 option 的格式新增下方程式在 console_init
中。此外也加入一個全域變數來記錄目前要使用的排序實作。
未來或許會有多款排序的實作,設計選項時,應予以考慮,亦即你可用 option sort 0
(預設), option sort 1
(上述來自 Linux 核心原始程式碼的 list_sort
), option sort 2
(未來實作的排序程式碼) 來切換。
jserv
在開始分析效能前。看到 Linux 核心的 list_sort
是以 bottom-up 方式實作 merge sort 並且合併方式是用對 cache 較友善的方式。即 就合併,大概可以猜測 Linux 的排序實作效能會比較好。我用以下的命令序列來分析排序效能:
為了避免出現 Time out 的問題。開始測試前會先 patch ./qtest,即 sed -i "s/alarm/isnan/g"
。
測試的結果如下表:
我的排序 | Linux Kernel 排序 |
---|---|
2.8899 | 2.1537 |
可以看到我的 top-down 排序演算法花的時間是 Linux 核心 list_sort
實作的 1.341 倍。即比 Linux 排序演算法慢 34% 左右。
Linux 核心是使用 bottom-up 的 merge sort ,但是它合併的方式跟傳統教科書不一樣。
TODO 補完此節
web
命令/網頁伺服器改善TODO
在作業說明裡面,每次抽到的數字後,都要從最前面開始往後數,這樣子會讓時間複雜度最壞情況至 ,故我採取不同的策略。
我先建立一個存放所有節點的指標陣列,如下所示:
可變更為 malloc(sizeof(*entries) * size)
以縮減程式碼。
jserv
已修改。 Commit 92d98c6
把所有節點的位址放入陣列中:
取得隨機數先直接使用 C 語言標準函式庫提供的 rand
函數取得隨機數。因為 rand
的範圍是 [0, RAND_MAX]
, RAND_MAX
不一定可以被候選數個數整除。故使用一個迴圈,丟棄任何會大於 RAND_MAX - (RAND_MAX % k)
的數值,使得取得的隨機數最大值可被候選數個數整除。讓每一個候選數被選中的機率是一樣的。以下是相關程式碼片段。這裡要取得 之間的數。
對照看 random.[ch]
程式碼。
jserv
這樣的實作缺點是需要使用 malloc
來配置額外空間。無法做到空間複雜度 。目前還沒想到能在時間複雜度為 情況下,空間複雜度為 的方式。
分析方式為建立一個 queue , 並分別新增 a, b, c, d 至 queue 中。執行 shuffle 1000000 次,並統計每種排列方式出現的次數。
直接使用作業說明提供的程式來分析。把作業說明的 Python 程式複製到 script/shuffle.py
,並修改改用 a, b, c, d 來進行 shuffle 測試。
排列 | 出現次數 | 期待次數 | |
---|---|---|---|
abcd | 41451 | 41666.67 | 1.1162907 |
abdc | 41827 | 41666.67 | 0.6169627 |
acbd | 41722 | 41666.67 | 0.0734827 |
acdb | 41524 | 41666.67 | 0.4884907 |
adbc | 41707 | 41666.67 | 0.0390427 |
adcb | 41439 | 41666.67 | 1.2439707 |
bacd | 41937 | 41666.67 | 1.7539227 |
badc | 41628 | 41666.67 | 0.0358827 |
bcad | 42129 | 41666.67 | 5.1300507 |
bcda | 41591 | 41666.67 | 0.1374107 |
bdac | 41626 | 41666.67 | 0.0396907 |
bdca | 41709 | 41666.67 | 0.0430107 |
cabd | 41418 | 41666.67 | 1.4840427 |
cadb | 41500 | 41666.67 | 0.6666667 |
cbad | 41712 | 41666.67 | 0.0493227 |
cbda | 41794 | 41666.67 | 0.3891307 |
cdab | 41439 | 41666.67 | 1.2439707 |
cdba | 42090 | 41666.67 | 4.3010667 |
dabc | 41767 | 41666.67 | 0.2416027 |
dacb | 41190 | 41666.67 | 5.4530667 |
dbac | 41823 | 41666.67 | 0.5865627 |
dbca | 41679 | 41666.67 | 0.0036507 |
dcab | 41625 | 41666.67 | 0.0416667 |
dcba | 41673 | 41666.67 | 0.0009627 |
Total | 25.17992 |
使用此線上工具計算 P 值,計算出來為 0.34 左右,故無法拒絕 Fisher–Yates shuffle 不是 Uniform Distribution。
跟據作業要求,執行以下命令來觀察 entropy。
大致上可以發現,在相同字串長度下,不重複的字母數愈多,其 entropy 愈高。例如: ciahh(21.88%)
, xffww(17.19%)
,也就是代表其亂度愈高。
從 entropy 公式可以發現,若字串中每一個字母其出現機率愈一致(即字串愈亂),且一字串所包含的不同字母數愈多 (即每個字母出現的機率一致),其 entropy 愈高,符合作業說明所說的。
不過到這裡,我還是沒有完全理解 entropy 的意義,目前的理解是:它用來表達一個訊息內容有多亂。之後還要再研讀。
qtest
亂數產生器存放在 random.c
,可以看到此亂數產生器是直接使用 Linux 核心提供的亂數產生器。
我跟據作業提供的 Wikipedia Xorshift 參考連結,並參考了相關的變形實作。我決定引進 Xoshiro256++ 來作為另一套亂數產生器。因為原本的 Xorshift 跟據維基百科說明,在統計上可能有些瑕疵。而其變種 Xoshiro256++ 在作者官網介紹中感覺看不出有瑕疵。
Xorshift 是 Linear-feedback shift register (LFSR) 的子集合
作者在他的網站上提供了 Xoshiro256++ 的程式碼,故我就直接使用作者網站提供的程式碼,再做點修改。此外作者建議使用 SplitMix64 作為初始化 Xoshiro256++ 的內部狀態,同樣也在他的網站上提供程式碼,我也直接把它拿來用。
把上述程式結合,再做點修改後,Xoshiro256++ 亂數產生器程式放在 xoshiro.c 檔案內。
另外再建立一個 qrandom.c
檔案,這裡是亂數產生器的單一入口,會再透過全域變數 qrandom_impl
來決定要使用的亂數產生器。而 qrandom_impl
可用 option random
來控制。目前設定是 qrandom_impl == 0
時使用系統提供的亂數產生器, qrandom_impl != 1
時就用 Xoshiro256++。
此外,我在 qtest.c
內實作 -r
選項,可讓 qtest
直接使用內部的亂數產生器,產生隨機 raw bytes 並輸出到 stdout 。-r 0
為使用內建的亂數產生器,-r 1
為使用 xoshiro256++ ,搭配作業說明 ent
工具來評估亂數產生器的品質。
在 main
函式處理命令列選項的 switch-case 地方加入:
另外也新增一個函式 generate_randombytes
,來把亂數輸出到 stdout 。
Xoshiro256++
亂數品質測試執行下面命令來測試亂數品質,可得到這個報告
卡方檢定的結果 p 值為 32.52% ,離 5% 遙遠,故此亂數產生器產生的亂數可能是 Uniform Distribution 的。
此外 entropy 值為 7.99998 bits per byte, 可說是相當高。
卡方檢定的結果 p 值為 60.75% ,離 5% 遙遠,故此亂數產生器產生的亂數可能是 Uniform Distribution 的。
此外 entropy 值為 7.999983 bits per byte, 也是相當高。
故二種亂數產生器均能產生品質不錯的亂數。
原本想要用 dieharder
測試,但跑了很久都沒有結束,所以後來就先提供 ent
報告結果。
你若對這主題感興趣,可對照閱讀 Uniting the Linux random-number devices,亂數產生器的品質,是近期 Linux 核心的重要開發方針之一。
jserv
dudect
統計原理dudect
會以二種不同類型的資料作為輸入,對待測的程式進行多次時間量測,並分別觀察二種資料作為輸入時其執行時間的分布,進行比較。這二種不同類型的資料分別是:
若固定輸入資料與隨機輸入資料的時間分布有顯著差異,則可以證明待測的程式其執行時間不是常數時間。反之則其執行時間可能是常數時間 (即無法拒絕其為常數時間)
執行 dudect
分為三步驟:
dudect
是用 Welch’s t-test
這份文件有其他人寫的論文說明也可供參考
需要再研讀 目前還是不懂 Welch’s t-test 和 Student's t-test 的原理,還需要再研究。
lab0-c
內 dudect
缺陷我發現 lab0-c
裡 dudect
有許多嚴重錯誤會導致偏差
lab0-c
的程式碼,發現的確沒有 percentile 相關程式。在原始的 oreparaz/dudect 會先計算 percentile 並把極端值除去。故在 lab-0
裡沒有正確實作這部份。constants.c
裡的 prepare_inputs
和 measure
設計錯誤。 prepare_inputs
裡的字串產生程式沒有辦法產生長度一致的字串,因為 randombytes 可能會讓字串中間有 '\0'
,此外 prepare_inputs
沒有跟據「固定資料」產生固定字串。prepare_inputs
雖然一開始產生隨機資料放在 input_data
中,但這份資料沒有在 measure
使用dudect
是直接用 rdtsc
指令來計算花費週期數,但計算的結果會包含程式執行到一半可能會被中斷去執行,因為系統上不是只有待測程式在執行。
lab-0
中 dudect
的改進改進方法如下:
dudect.h
引入我的程式,但把它拆成 dudect.c
和 dudect.h
。dudect.c
內的 randombytes
和 randombit
的函式移除, 因此在 random.c
就有提供相同的函式了。dudect
原本是設計一個 binary 只能有一種測試,因為它會直接呼叫 prepare_inputs
和 do_one_computation
這二個由使用者實作的函式,但因為 lab0-c
有四個待測物,故我把 dudect_config_t
內加入 prepare
和 compute
二個存放 function pointer 欄位,然後把原本呼叫 prepare_inputs
和 do_one_computation
改成呼叫 dudect_config_t
內的 function pointer 。DUDECT_NOT_ENOUGHT_MEASUREMENTS
這個狀態。並修改 report
在還沒收集足夠資料時,回傳 DUDECT_NOT_ENOUGHT_MEASUREMENTS
。而 dudect_main
函式中,預設也是在還沒測試時回 DUDECT_NOT_ENOUGHT_MEASUREMENTS
measure.c
和 measure.h
,放置待測程式及 dudect_config_t
的相關設定。並且也實作 is_##op_const
巨集用來產生 is_xxx_const
函數供 qtest.c
內的程式呼叫。此外把 CHUNK_SIZE
提升到 640 使測試更準確。因為版面有限,就不把程式碼貼出來,可點連結觀看。完成以上步驟後,make test
的結果正確回報為執行時間為常數時間。
請提交 pull request。
jserv
可再進一步實驗
perf_event_open
搭配 config = PERF_COUNT_HW_CPU_CYCLES
量測時間。看看能不能更能排除待測程式執行時被中斷的執行時間。labc-0
中的 dudect
程式做修正,而不是直接引入上游最新版和重寫測試程式。我檢視我的 repository 的 GitHub Action 有無正常運作時,發現 GitHub Action 在 Run webfactory/ssh-agent@v0.7.0
這個步驟時就失敗了。
於是我就看了一下原始的 sysprog21/lab0-c
repository 。裡面的 GitHub Action 可以運作成功。發現原來 Run webfactory/ssh-agent@v0.7.0
是用來載入 ssh private key 讓原始 sysprog21/lab0-c
repository 在沒有 queue.c
的解答情況下,能從另一個 private repository 拷貝 queue.c
來使用,使原始的 sysprog21/lab0-c
repository 的 GitHub 在沒解答情況下也可以測試。
但是在 fork 之後的 repository ,不會有 ssh private key 。導致在 Run webfactory/ssh-agent@v0.7.0
步驟就失敗,以致後面的 make check
、 make test
等步驟都不會執行。
我看了 GitHub 的文件後,發現可以把 GitHub Action 某個步驟標示為即使失敗仍然能繼續執行。故我在 .github/workflows/main.yml
裡進行小修正,針對 webfactory/ssh-agent@v0.7.0
這個步驟加入 continue-on-error: true
來讓它失敗時,也可以往下執行其他 GitHub Action ,使得即便沒有 ssh private key , GitHub Action 也能有作用。
我為此建了一個 pull request 來提交我的改動,目前已經被 merge 了。
return true;
in q_quit
functionq_quit
一開始就 return true;
導致後面記憶體釋放的程式沒有執行,以致於觸發 valgrind memory leak。此 pull request 改善此問題line_atexit
in linenoise
functionline_atexit
內會呼叫 free_history
釋放記憶體。但在 stdin 非 tty 時, line_atexit
不會被註冊,以致於使用檔案輸入指令時,沒辦法釋放 linenoise
記憶體,導致 linenoise
出現 memory leak 。此 pull request 修正此問題。Merge do_i(h|t)
into a single functionTODO 放 GitHub Pull requests
要 commit 時發現在 report.c
中的 static char *msg_name_text[N_MSG]
變數宣告會觸發 cppcheck 錯誤。原本打算把此行改為 static char * const msg_name_text[N_MSG]
,但後來發現 GitHub 有更新新的程式碼加入 // cppcheck-suppress constVariable
修正此問題,因此我直接在 GitHub 網頁介面上操作 sync fork 取得更新的程式至自己 fork 出來的 repository 。
因為自己只寫了一點點程式,故直接捨棄本地端的 commit,用 GitHub 上新的程式覆寫本地的 repository 。
可善用 git rebase
jserv
謝謝老師,我會嘗試練習
git rebase