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
2022q1 Homework1 (lab0)
contributed by <
LJP-TW
>實驗環境
作業要求
實作 queue
嘗試盡量使用 Linux 核心風格的鏈結串列 API 進行各函數的實作。
q_new
檢查是否能分配空間,並且做
list_head
的初始化。q_free
釋放整條 queue 所占用的記憶體,由於都要歸還記憶體了,已經無需再將 element 從 queue 中移除鏈結。
q_insert_head
從 queue 的頭部新增節點,檢查是否能正常分配記憶體,字串長度需要為了 NULL byte 預留空間。
q_insert_tail
與
q_insert_head
類似。q_remove_head
從 queue 的頭部移除一個節點,複製字串到
sp
時要預留 NULL byte 的空間。q_remove_tail
與
q_remove_head
類似。q_release_element
沒有做改動。
q_size
需要走過 queue 計算長度,時間複雜度為
O(n)
。q_delete_mid
用兩個 pointer 走過 queue,
curr
走訪速度為mid
兩倍,因此當curr
到達 queue 的結尾時,mid
自然是在 queue 的中間,找到mid
後刪除該節點。q_delete_dup
呼叫此函式前,queue 已經排序完畢,因此走訪過程中,只需要判斷下個節點的值是否與當前節點相同,作為是否要刪除節點的依據。在
do
while
後,還要再刪除currelm
,但這個寫法不夠簡潔,還有改善空間。q_swap
每經過兩個節點就交換兩者在 queue 中的順序。
list.h
沒有與交換節點相關的工具能夠使用,或許能夠增加相關工具 macro / function。q_reverse
將整個 queue 的順序倒轉,只要交換每個節點 (包含
head
) 的prev
和next
即可。TODO: 針對上方的若干操作,提取出可共用的 inline function
- 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 →q_sort
使用 merge sort 進行排序,先將 doubly-linked list 的
prev
和next
指標拆開來變成兩個 singly-linked list,以next
組成的 singly-linked list 用來串接排序好的節點,形成一個個獨立排序好的 sublists,再以prev
組成的 singly-linked list 串接每一個 sublists,即可在不額外配置記憶體的情況下完成 merge sort。在 qtest 實作命令 shuffle
首先先看一下要如何增加一個命令,在
qtest.c
搜尋ih
,試圖從ih
命令怎麼實作的下手,可以看到有do_ih()
函數:以及在
console_init()
中大量使用ADD_COMMAND
:檢查
ADD_COMMAND
macro,位於console.h
:command 是「命令」、instruction 是「指令」,請查詢英語辭典
- 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 →add_cmd
會在命令列表cmd_list
中添加新命令,明白要新增一條新命令shuffle
則要實作do_shuffle
,並在console_init()
替新命令加上ADD_COMMAND
。在查看
swap
、reverse
命令的實作時,發現在呼叫到主要邏輯q_swap
、q_reverse
前後都有特定的函數呼叫:在閱讀了 K01: lab0 - Signal 處理和應用後明白這部分是設定發生 exception 時的處理方式,這邊實作
shuffle
時也可將主要邏輯以set_noallocate_mode
和exception_xxxx
包住。shuffle
主要邏輯:新增命令:
在 qtest 實作命令 web
提供命令
web
,並可以用參數指定 listen port,預設 port 為 9999:使用範例可以用
curl
進行測試:新增了
web.h
和web.c
,將從 tiny-web-server 的主要程式碼放置於此。新增檔案後需要修改Makefile
:值得一提的是,在
process()
尾端要另外配置記憶體,並複製字串的部分如下:而
req.filename
的設定來自於parse_request()
:這邊最長能往
req->filename
寫入MAXLINE
個字,也就是 1024,而req
為http_request
結構,其原始定義如下:req
為process()
的區域變數,這就導致了有 stack-based buffer overflow 的漏洞,目前修正方式是將http_request
定義方式改成如下:在
qtest.c
新增do_web()
:參照 K01: lab0 - 整合 tiny-web-server 的說明進行
run_console()
和cmd_select()
的修改。首先是
run_console()
,當 web server 開啟時,則改成通過cmd_select()
進行輸入的選擇:cmd_select()
當 server socket 有輸入時則要接收連線並處理:使用結果 (
qtest
):Client 端:
開發過程
list.h
觀察
lab0-c
中的 list.h,與 Linux source code 中的 include/linux/list.h 很像。先閱讀了一下此檔案,一方面看是否有能派上用場的工具,一方面先詳讀每個工具以避免實作時錯誤使用。container_of
為何要寫
__typeof__
在 6.7 Referring to a Type with typeof 提到:但這邊我真正的疑問是為何要分成兩種做法,實際使用兩者,編譯成執行檔,查看組語:
組合語言:
在 6.48 Alternate Keywords 中提到,在使用
-ansi
或-std
的情況下,asm
、inline
、typeof
這些關鍵字不支援,解法是需要在這些關鍵字前後加上雙底線__
。typeof
為編譯器提供的功能,而非原本就在 C specs 中的關鍵字,因此使用typeof
要看你用的編譯器有沒有支援,而__typeof__
為 GNU gcc 提供的功能,在其他編譯器可能沒有,這種情況下可以用 macro__GNUC__
來判別編譯器是否為 GNU gcc,如下例子:但以上例子成立的前提是你使用的別款編譯器要支援
typeof
。回過頭來解釋
container_of
,裡面用到的offsetof
就有在 C specs 中,所以其一邊的定義是用了 GNU gcc 功能,一邊則是完全遵守 C specs,但似乎還是沒解釋到為何不單純用遵守 C specs 的定義就好。list_entry
在
qtest.c
中的使用範例,可以直接從element_t
的list
反推取得element_t
的位址:從以上範例也可觀察到,
element_t
中的list
,不管是prev
或next
都應該指向到element_t
中的list
。list_del
若有啟用
LIST_POISONING
,則可以防止被移出 list 的 node 的prev
和next
仍然指向有效的 node,進而防止類似 Use-After-Free 的攻擊。list_splice
將 list 中的 node 加到 head 的起頭,隨後若要再使用 list 需要重新初始化。
list_for_each_entry_safe
迴圈經過所有在 list 中的結構 entry 本身,而非用來連結的 pointer。
但是定義中還是使用到了 GNU gcc 的額外功能
__typeof__
,所以才註解了FIXME: remove dependency of __typeof__ extension
。list_sort.c
linux/lib/list_sort.c 部分程式碼:
這邊非常巧妙,可以使得 pending lists 滿足幾個假設:
pending
以prev
連結成一個 singly linked list,每個prev
指向的是一個 size \(2^k\) sublists,等待著被合併。以下表格逐步說明 count 增長時,pending lists 內部中各個 sublists 的變化,每一個數字表示一個 sublist size,寫在越後面的表示是在
pending
這張 singly linked list 越前面的位置:再對比自己的實作,發現自己的實作問題是在 worst case 的情況下,在 merge 時兩邊 list 的 size 沒有限制,導致單次 merge 最差的時間複雜度會是 \(O(n)\);反過來看 Linux 的實作方式,最差的狀況兩邊 list size 為 \(2^{(k+1)}\) 和 \(2^k\),單次 merge 過慢的發生機率就會顯著下降。
trace-17-complexity
在完成 queue 的實作後首次推上 github,CI 的結果不如預期。在本地端測試時,主要是 trace-14-perf 一直過不了;但 CI 的結果反而是卡在 trace-17-complexity,而這部分是測試
q_insert_tail
、q_insert_head
、q_remove_tail
、q_remove_head
是否為 constant time,這我反而很有信心應該要通過。錯誤訊息中有大量的not enough measurements
訊息,可能與之相關。第二次重新跑一次 CI 就通過了,需要再研究為何會有這樣的結果發生。TODO: 對照閱讀論文
- 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 →