contributed by <AlecJY
>
commit message 中,將 "Complete" 改為 "Implement" 更為適當
課重助教好的,謝謝助教的建議
AlecJY
查詢指令及結果如下
make
,會編譯程式碼並且把 git hook 設定好cppcheck
以及 clang-format
,因為使用 openSUSE , clang-format
直接放在 clang5
這個 package 裡面,安裝指令如下這邊根據註解的提示加了檢查 malloc()
是否回傳 NULL
的檢查,如果是 NULL 的話就直接回傳變數 q
,避免之後存取到 NULL
指標。
q_insert_head()
這部分就是檢查 queue 和字串是否為 NULL
,然後利用 strlen()
計算字串長度後使用 malloc()`` ,新增一個
list_ele_t` 之後加到 queue
q_remove_head()
這部分也是檢查 queue 和字串是否為 NULL
,然後在 sp
不是 NULL
的情況下把 head 的值複製過去,這邊使用 strncpy
複製,然後留下最後一個位置放 null character ,避免字串沒有結尾,之後把 queue 的 head 指到新的 head 後把舊的 head free
在複製字串的部分忘記文件有提到過一定要用 malloc()
來分配空間,所以原本想要用 strdup()
的,結果在 free 的時候就直接 segmentation fault ,原本還以為誤會了 strdup()
的用法,所以仔細檢查了一遍發現好像沒有問題。後來想說不透過 qtest 直接呼叫 function 操作看看,在把測試用程式寫完想要編譯的時候發現出現底下的錯誤
去看了其中一個 include 的 header harness.h
的時候才發現原來 malloc()
和 free()
被 Macro 改成了 test_malloc()
和 test_free()
,所以 strdup()
呼叫原本的 malloc()
所創建的記憶體空間被不知道做了什麼事情的 test_free()
釋放掉,所以產生錯誤,所以嘗試把 harness.h
註解掉之後用自己寫的測試小程式跑發現就沒有產生 Segmentation Fault 了
等把程式寫完去看 qtest 程式碼後會再補上
test_malloc()
和test_free()
做了什麼
大致上步驟跟 q_insert_head()
差不多,由於要求時間複雜度為 O(1) ,所以在 queue_t
中新增了一個新的欄位 tail
來記錄 queue 的結尾,並且同時更改了 q_insert_head()
和 q_remove_head()
在新增至空 queue 和將 queue 清空的情況變動 tail 中的值
這部分在 queue_t
中新增了一個欄位 size
來記錄 queue 的長度,一開始先初始化成0,當有新增或刪除的動作時再將 size
的數值做增減
這邊使用了一個迴圈走訪 queue 中的所有元素並把他們以及他們裡面值得記憶體空間釋放,最後再將 queue 本身釋放
這邊先把 tail 的值設定為反轉前的 head ,再使用一個迴圈走訪 queue 並移動 head 的位置,移動時把 next 改為前一個值的位址,走訪完的時候由於最後一個元素因為條件判斷的關係沒有更改到 next 值,所以在迴圈執行完後單獨處理
make
的時候會檢查 .git/hooks/applied
是否存在,如果不存在就執行 scripts/install-git-hooks
這個腳本install-git-hooks
會在 .git/hooks/
這個資料夾裡面建立 pre-commit
和 commit-msg
的 symbolic link,並建立 .git/hooks/applied
pre-commit.hook
harness.c
/ harness.h
因為之前被改過的 malloc()
和 free()
弄到產生 bug ,因此想說先從這兩個檔案開始看
harness.h
裡面有一段程式碼
在有定義 INTERNAL
的時候就不會將 malloc()
和 free()
取代成 test_malloc()
和 test_free()
了,在 harness.c
和 qtest.c
都有定義 INTERNAL
以避免使用 test_malloc()
和 test_free()
在 test_malloc()
裡面會檢查現在是否處於 noallocate_mode
,並根據機率隨機決定是否要讓 malloc 強制失敗,之後會建立 block_ele_t
的變數 new_block
,test_malloc()
實際上回傳的記憶體空間是 block_ele_t
中的 payload
,並在 payload
的前後塞一些固定的值作為檢查,之後把 new_block
和其他之前 allocate 的 block 串起來
在 test_free()
裡面會透過 find_header()
算出 block_ele_t
這個資料結構真正的位置,並且確認是否真的存在,檢查 magic_header
等處理,之後檢查 footer 有沒有錯誤,跟檢查有沒有發生 buffer overflow 而放在 stack 裡面的 canary 有點像,最後把 header 和 footer 都設定成 MAGICFREE
之後把整塊空間釋放
在 exception_setup()
裡面有用到一個 sigsetjmp()
,如果回傳 0 的話就代表它是直接 return ,如果不是 0 就代表是使用 longjump()
或是 siglongjump()
過來這裡的,這邊利用這種方式設定一秒的限制執行時間,之後在 qtest.c
裡面設定 SIGSEGV
和 SIGALRM
的 handler 來處理 Segmentation Fault 和執行超時。
console.c
/ console.h
這邊使用了一個 list 紀錄指令,利用 add_cmd()
,在裡面放入指令名稱、執行指令的函式以及說明,之後在 interpret_cmda()
的時候就會去 list 中找到對應的指令並呼叫函式