contributed by < rwe0214
>
Linux
, rwe0214
, NCKU
在實作過程發現,如果想在 q_insert_tail
達到 的時間,不在 queue_t
structure 中新增其他的 field
去紀錄額外資訊是很難達到的,所以新增了一個 list_ele_t *tail
指標記錄尾部的位址。
在 queue
為空的時候回傳 zero ,其他狀況回傳 queue size,使用 !!
可以確保 queue
不為 NULL
的時候,條件判斷式的值為 1。
需要處理 malloc 失敗的情形。
queue
為空無動作。
一開始在 strncpy
複製字串時使用 sizeof(s)
作為字串大小,後來發現這僅僅會回傳 char *
的大小,而不是字串真正的記憶體大小,另外為了防止字串無法終止,所以額外多一個位元組 (byte) 存放 null terminator
。
在沒有在 queue_t
中新增 tail
pointer 前,一直無法在時間複雜度上有所提升,皆為 ,新增後可達
但是上述在跑 complexity test 時會不正常結束,使用 valgrind 分析後會發生 Invalid write of size 8
,發現是因為原本的程式碼在新增流程中出錯,
更改為以下:
依照註解完成,目前還沒搞懂為什麼需要 char *sp
和 size_t bufsize
,推測是和實作 q_reverse
有關。
程式碼是提供給你修改用,而非鑑賞的,有疑慮就去做實驗
在實作這個的時候,因為提示的註解暗示可以使用 q_insert_head
, q_insert_tail
, 和q_remove_head
來完成,但是目前我對如何使用他們還沒有頭緒,所以先暫時實作一個利用 pointer
暫存前後 elements
的方式來完成
一開始的想法是單純使用隨堂考 quiz1 的排序方法,並把它改成 iterative 的版本,但發現執行時間一樣逾時。
不難發現 quiz1 提供的程式碼存在嚴重缺陷,並非典型的 merge sort,特別在時間複雜度的表現上
分析時間複雜度發現,原本以為 quiz1 是一般的 merge sort,但他的 divide and conquer 並不是切成對等的兩個子問題,說明如下:
所以重新撰寫一個針對 linked list 的 merge sort 實作: q_sort。
TODO:
我把 CSAPP 中有談到 RIO 的部分整理在 CSAPP Ch10 筆記
console.c/cmd_select
中利用 select
去監視 readfd set 中是否有哪個 readfd
是已準備好資料進行讀取的,程式碼片段如下,可以看到在
去對已經加入 readset 的 fd 去做監視,並在有準備好資料輸入的情況下 ( after typing <ENTER>
),進入下面的迴圈進行
而在 readline
的實作中,就運用了 RIO package
的原理和方法,
可參考 CSAPP Ch10 筆記 buffered I/O function
TODO: 除了研讀程式碼,能否修改程式碼並搭配 GDB 進行實驗?
此 api 透過
作為呼叫入口,之後依照 terminal
輸入環境區分成三個運作方式
TODO: HackMD 支援 GraphViz 語法,請改寫以下示意圖。
enalbeRawMode
中修改 STDIN 的設定,使其能在 raw mode 下輸入,終端機介面又稱 tty 介面,分成兩種模式,一種為正規模式 ( cooked mode ) ,另一種是非正規模式 ( raw )
cooked mode
Linux
的 shell
指令。raw mode
Linux
使用 vim
編輯程式。termio 可以透過 termios 來設定 ( 如下 )
透過 bits 的調整來達到設定
這部分讓我聯想到之前在 Microchips 課堂中也是透過 bit 來控制 Microchip 的行為
在
使用 TCSAFLUSH
option 將 fd
flush
後設定剛剛的 termio
實作的想法如下
lab0-c
直接使用修改後的 linenoise
( i.e., 捨棄 readline
,將 readline
替換成 linenoise
,並修改 linenoise.c
使其符合 lab0-c
的環境 )上面的想法來自於 linenoise 的 README.md 將他的使用方法講的非常簡當,而 example.c 也是…,但是目前卡關中xD
[solved]
Thu, Feb 27, 2020 3:53 PM
首先了解在 lab-0c/qtest.c 中是如何處理輸入的,
在進入 console.c
之前,會先對需要的設定進行 initailize ( e.g. 處理 option, queue_init
, init_cmd
, console_init
… ),並在 run_console 將 input_file
傳入,
在 cmd_select
中利用 select
去監視 readfd set 中是否有哪個 readfd
是已準備好資料進行讀取的,程式碼片段如下,
嘗試將20行替換成以下,目標是先在 STDIN 的輸入下,單純將讀取方式更改為 linenoise
但卻需要重複兩次輸入才能在將 STDIN
寫進 linenoise
進入 GDB 後發現,
在執行到 select
時,select
會如預期的監視 STDIN
有沒有資料輸入,但是輸入後又會再 linenoise
處再停等一次,而不是將 STDIN
上已有的資料讀取。
目前在這裡卡了一陣子,目前的想法是跟
linenoise
會去設定STDIN
的模式的影響,但是我把raw mode
關掉後卻只會立即輸出指令
重新思考 linenoise
的運作模式,是直接對 STDIN
做編輯和修改 ( i.e., 在還沒輸入 <ENTER>
之前即在進行 ),而 select
則是會在輸入 <ENTER>
之後才會認為 STDIN
已經準備好 ( 因為 STDIN
是 line buffered ),而 linenoise.c/enableRawMode
中的
會將 STDIN
flush 掉,所以才會需要重新輸入一次。
但在只使用 STDIN
的情況下,做好在讀取之前的buffer flush
( 這部分 linenoise.c/enableRawMode.c
會幫我們做 ),就可以拿掉 select
後就直接使用 linenoise
了。
但是在讀檔的情況下也能拿掉 select
嗎?如果檔案是一個正在編寫中的文件就無法得知檔案目前是否已經資料準備好,所以仍然需要 select
。
另外在執行時發現 linenoise
不會將 "\n\0"
回傳,但是在 cmd interpret 的時候需要以 "\n\0"
做判斷,再加入
接完後就剩把 linenoise
中的 complete
功能在執行 linenoise
初始化。
先看看 linenoise
怎麼實作 complete
的,可以看到在 completeLine
的實作方式是用一個新的結構體 linenoiseCompletions
len
指的是有著同樣開頭的 cmd
數量,而 cvec
則把同樣開頭的 cmd
以 linked list 串起來。所以我們只需要設定這個結構體即可,而在 linenoise
是用 callback 的方式去定義這個設定 function,需要先自行寫好再去 callback
並在初始化 console 的時候呼叫
如此一來,就可以在 completeLine
中直接使用
如此便完成了。而 history 則沒那麼複雜,直接使用 char
陣列紀錄。
以下的程式碼為 console.c
中片段,
上面程式碼的 for-loop 中有關使用 read
的控制是在第 11 行 if
而 buf_stack->cnt
在最初 init 時會是 0
,毫無疑問的可以進入 read
的步驟,並將 buf_stack->fd
讀進 buf_stack->buf
,之後再透過判斷 read
回傳值更新 stack_buf->cnt
,根據 linux manual,read
的 return value <= 0 會有兩種情況:
EOF
and return 0
ERROR
and return -1
但在上面的程式碼片段中,只對遇到 EOF
的情況做處理,也就是說當 read
發生未預期的錯誤時,只把它當成 EOF
的狀況處理。
根據 CSAPP Ch10 提到 RIO 應該是在 read
的時候如果沒能完成任務 ( short has been happened ) ,應該繼續的嘗試 read
,所以在程式中加入判斷是何種情形以及將十一行的 if
替換成 while
。
如下,
TODO: 解釋如此修改的 side effect 以及你如何驗證