or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
2020q1 Homework1 (lab0)
contributed by <
MetalheadKen
>- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →實驗環境
題目摘要
Some of the skills tested are :
Implementing a queue
Resources
The task is to modify the code in
queue.h
andqueue.c
to fully implement the following functionsq_new
: Create a new, empty queueq_free
: Free all storage used by a queueq_insert_head
: Attempt to insert a new element at the head of the queue (LIFO discipline)q_insert_tail
: Attempt to insert a new element at the tail of the queue (FIFO discipline)q_remove_head
: Attempt to remove the element at the head of the queueq_size
: Compute the number of elements in the queueq_reverse
: Reorder the list so that the queue elements are reversed in order.q_sort
: Sort elements of queue in ascending order.Precautions
NULL
. An empty queue is one pointing to a valid structure, but thehead
field has valueNULL
malloc
to allocate space and then copying from the source to the newly allocated spaceq_insert_tail
andq_size
will require that your implementations operate in time \(O(1)\)q_reverse
should not allocate or free any list elements. Instead, it should rearrange the existing elements實作流程
queue.h
依據題意,為了讓函式
q_insert_tail
和q_size
的時間複雜度為 \(O(1)\),在 structqueue_t
裡新增成員tail
和size
,使得在 runtime 的時候可以動態紀錄 queue 的尾端和其大小q_new
需注意若
malloc
失敗時需回傳 NULLq_free
釋放 queue 的空間,注意記得一併釋放節點和字串所佔的記憶體空間
q_insert_head
malloc
失敗時,在回傳 NULL 前需釋放之前已經配置好的記憶體空間goto
來作錯誤處理,可參考 goto in Linux kernel coding style 章節q_insert_tail
q_insert_head
的實作方向相同tail
來串接新節點tail
的 data type 為list_ele_t**
,使得可以減少 branch penalty,可參考q_remove_head
sp
不為 NULL 時代表需把移除的字串複製到sp
中sp
的大小,因而最多只需複製長度為bufsize - 1
,並注意要在結尾上加上 terminating null byteq_size
size
動態紀錄 queue 的大小,並在此函式直接回傳size
的內容new
command 的情況下直接使用size
command,所以需判斷若 queue 為 NULL queue 或 empty queue 則直接回傳數值 0q_reverse
prev
,curr
,next
來紀錄反轉的過程prev
紀錄 queue 前一個的節點curr
紀錄 queue 當前的節點,在反轉過程中,curr
的下一個節點應指派為curr
的前一個節點next
紀錄 queue 下一個的節點head
和tail
需重設方向q_sort
merge_sort
裡面透過fast
和slow
pointer 來找到該 list 的中間點為何Implement Natural Sort
這邊採用 sourcefrog/natsort 所實作的 natural sort 的 compare function -
strnatcmp
。從原專案中取得strnatcmp.[ch]
後放入lab0-c
的專案中。這邊為了明確的表示是使用別人的專案,原本是想採用 git submodule 的方式建構的,但是考慮到若要對 natsort 專案做改動,勢必要 push 回去到原專案中,或者在當初 submodule init 時只能指定 fork 後的專案,但都相對的要麻煩許多。
不知道有什麼方式可以除了把有 reference 到的專案之相關訊息寫進 commit message 以外達到明確的使用到以及隨時跟新進度在別人的專案上。
這是練習撰寫清晰的 git commit messages 的好機會
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →接著把在排序中採用到的
strcmp
一律改成strnatcmp
qtest.c
queue.c
更改
Makefile
,使其可以編譯strnatcmp.c
Makefile
為了對現有的 natsort 做測試,增加新的 trace file
traces/trace-18-natsort.cmd
script/driver.py
對
trace-16-perf.cmd
採用到的RAND
提供英文與數字的亂數排序,其實只要新增數字到charset
就可以了qtest.c
執行
make test
後出現以下錯誤,依錯誤訊息可得知為超時這是因為在 natsort 中所使用的
strnatcmp
比 glibc 所提供的strcmp
的執行時間還要長的緣故。為了解決這個問題,這邊採用的方法為新增一個新的 command optiontime
,藉由option time <limit=1>
可以指定在這次測試中所會參考的 time limit。qtest.c
為了要讓
time_limit
可以動態更改,必須把time_limit
改為 global variableharness.c
harness.h
加入
option time
到會因為 natsort 的緣故而造成 Time limit exceeded 的 trace 中traces/trace-15-perf.cmd
Address Sanitizer
執行
make SANITIZER=1 test
後出現下列錯誤先用 GDB 定位問題
從這邊可以知道由於
simulation
從bool
轉成int *
並存入valp
,之後當直接 dereference 的時候就會因為要取出 4 bytes 的資料而存取到不該存取的地方故造成 buffer overflow為了要解決 global-buffer-overflow,我們需要把轉型成
int *
的地方改為void *
,根據 C99 規格,pointer to void 做為一個 generic pointer 可以把原本的 object type 轉成 pointer to void 再轉回來。藉由於此,我們可以透過 pointer to void 來做到不同的 data type 的存取除了
void *
來儲存各種的 data type 以外,還需要一個 variable 來儲存原本的 object 所佔的大小。最後,根據上面所述,我們需要修改以下動作來解決所需的問題PELE
來儲存void *
和原始的 object sizevoid *
和原始的 object size 的動作void *
可以透過原始的 object size 來轉型回原來的 data type修改如下
console.h
console.c
qtest.c
Valgrind + Massif Heap Profiler
安裝 massif-visualizer
$ sudo apt install massif-visualizer
使用 valgrind + massif 進行測試
使用 massif-visualizer 視覺化
$ massif-visualizer massif.out
Dudect 相關研究
select 系統呼叫
自動評分系統相關補充
如何呼叫使用者輸入命令對應的函式
一開始使用者執行
qtest
後,輸入命令時,會先執行console.c
中的interpret_cmd
並呼叫parse_args
來將輸入的字串以空白為區隔,轉換為argc
和argv
,如下之後在函式
interpret_cmda
中,利用迴圈走訪 singly linked list,並利用strcmp
找出函式相對應的字串。可以發現在qtest
中使用函式指標的方式可以很簡單方便的維護使用者可輸入的命令在寫好 trace file 後程式如何運作
在
qtest
中有提供參數-f <filename>
來讀取 trace file。當使用者輸入命令後,在qtest.c
中的main
函式會呼叫getopt
來解析使用者先前輸入命令的後面所添加的參數,節錄如下getopt
是一個可以很方便的去 parse 從 command line 傳過來的參數的函式,該函式會一直讀取參數直到沒有更多的參數可以讀時才回傳 -1int getopt(int argc, char *argv[], const char *optstring)
int argc
: the argument count passed to themain()
functionchar *argv[]
: the argument array passed to themain()
functionconst char *optstring
: a string containing the legitimate option charactersgetopt
回傳數值後optstring
找不到 option 字元則回傳?
,在optstring
設定需帶參數的 option 沒有帶參數的話則回傳:
optarg
變數儲存該 option 後面所帶的參數optind
變數儲存下次呼叫getopt
時要處理的 argv 的 indexoptopt
變數儲存當getopt
找不到 option 字元或是缺少某些參數時的該 optiongetopt
是 POSIX standard 但是並不是 C standard 並在 glibc 實作中的某些行為是 GNU extensions,這點需要特別注意在
console.c
中定義以下 structure 來儲存每個要使用的檔案的 file descritptor並在
push_file
函式中把開啟檔案的 file descriptor 放進struct RIO_ELE
中的fd
,若無指定開啟檔案的路徑則選擇為標準輸入 (也就是stdin
)如何偵測記憶體洩漏
harness.[ch]
中藉由替換掉malloc
和free
的實作方式使得可以檢查到 allocation error從
harness.h
中可以見到qtest
使用 macro 來把malloc
和free
替換成test_malloc
和test_free
而從
test_malloc
所配置出來的記憶體都會被紀錄在struct BELE
中,structure 在harness.c
中定義如下並以 doubly-linked list 來串接節點在 structure 中
payload[0]
是一個 struct hack,在 GCC manual 中稱呼為 Arry of Length Zero,藉由這種方式可以做到讓 structure 中的成員可以動態的配置記憶體又不用因為多次呼叫malloc
造成記憶體碎片化用法如下
這樣的手法其實就是利用 GNU C compiler 預設不會去檢查 Array index out of bounds 的問題來做到的,但是使用這個技巧需要思考以下的問題
sizeof
不能用在 incomplete type 上另外在 ISO C90 可以使用 Array of Length One 來做到同樣的事情,而從 ISO C99 開始支援 Flexible Array Members,請參照
如何配置記憶體
test_malloc
把經由malloc
配置出來的新的記憶體空間,將其串連到 doubly linked listallocated
中,其中配置出記憶體空間的寫法如下可以看到除了
size
和sizeof(block_ele_t)
以外,還多了sizeof(size_t)
,這是因為在qtest
中,藉由在尾端多配置出一個空間並填入 magic number 來查看若當該數值有被更動過的話,即表示出現 memory corruption (array access out of bounds),以及偵測是否是由test_malloc
配置出來的記憶體空間,而前一個成員magic_header
也是基於此用途的。harness.c
中的test_malloc
節錄如下在
fail_allocation
中,實作了當亂數產生出來的數小於 0.01 乘上預先設定好的失敗機率 (fail_probability
) 的話,即 malloc failure,為何需要如此實作?回去複習機率統計,思考上述做法是否合理,若有疑慮就提出論證和實驗
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →其中為了方便取得到
payload
的尾端來指派 magic number (MAGICFOOTER
),qtest
實作了find_footer
函式,如下藉由傳進來的
block_ele_t *
的開頭,在加上 payload size 和該 structure 本身的大小來取得到payload
的尾端