# 2021q1 Homework1 (lab0) contributed by < `OscarPolai` > ###### tags: `week1`,`lab0` :::danger 注意共筆書寫規範:中英文間用一個半形空白字元區隔 :notes: jserv ::: ### 影片 timestamp 紀錄 [lab0作業導讀](https://hackmd.io/@Polai/BycAjNzzd) , [2020 lab0 code review](https://hackmd.io/@Polai/By_8Qw7fO) ### 下載工具 [參考頁面](https://github.com/sysprog21/lab0-c) * ==下載套件== $ sudo apt install build-essential git clang-format cppcheck aspell colordiff valgrind >之前沒玩過 linux , 紀錄一些疑問. >1.為何打 apt install 加上 這個軟件的名稱就可以下載 >2.看起來這個命令可以一次下載全部的 >3.為何 apt-get update 可解決踩雷1? * 踩雷紀錄 1.打入下載命令一開始沒有下載完成 -> E: 有部分套件檔無法取得,試著執行 apt-get update.. -> 無權限 -> 多加一個 sudo 結果執行完後,再執行一次命令就下載成功了 , >valgrind --version -> 3.13.0 >Cppcheck --version -> 1.82 :::warning Update : 後續在執行 `$ git commit -a ` 會發生問題 , 原因在於 Cppcheck 沒有高於 v1.90. ::: ### Cppcheck 套件重裝 $ sudo apt remove cppcheck $ git clone git://github.com/danmar/cppcheck.git $ cd cppcheck $ make $ sudo cp ./cppcheck /usr/bin/ $ cppcheck --version Cppcheck 2.3 * 若用上述方法裝 Cppcheck 發現會找不到某些檔案 `E : cppcheck: Failed to load library configuration file 'std.cfg'. File not found (information) Failed....` * 發現 Ubuntu 不是 20.04 LTS -> Update Ubuntu 版本 ### 更新 Ubuntu 版本 [更新 Ubuntu 方法](https://zhuanlan.zhihu.com/p/137110647) , [刪除不必要內核](https://www.arthurtoday.com/2013/05/ubuntu-remove-all-older-linux-kernels.html) * 更新 Ubuntu v20.04 -> / 底下的空間不夠 * 刪除不必要舊內核 -> 快速命令 -> $ sudo apt-get remove $(dpkg -l|egrep '^ii linux-(im|he)'|awk '{print $2}'|grep -v `uname -r`) * 再重新安裝 Cppcheck 套件,版本回到 v1.90 就可以執行 `$ git commit -a ` $ sudo apt install build-essential cppcheck ### 取得程式碼並進行開發 * step1.建立資料夾 > $ cd ~ > ![](https://i.imgur.com/462IYQj.png =400x265) [reference](https://miahsuwork.medium.com/%E7%AC%AC%E4%B8%80%E9%80%B1-command-line-%E5%9F%BA%E6%9C%AC%E6%8C%87%E4%BB%A4%E8%88%87%E6%93%8D%E4%BD%9C-f4da8bcfdfa) > $ mkdir -p linux2021 >==??為何要加-p ??== :::warning 你可以試著在同一個目錄下,執行 `mkdir linux2021` 二次,看會遇到什麼錯誤訊息。反之,`mkdir -p linux2021` 多次執行卻不會見到錯誤訊息。接著你可執行 `man mkdir` 以查詢細部用法。 :notes: jserv ::: * step2.自 github 取得專案 > $ git clone https://github.com/sysprog21/lab0-c 1.該資料夾就多了好多檔案->看起來是把上述網址的專案下載到我的主機該資料夾 2.cd linux2021(到該資料夾進行clone,否則會跑到其他地方) * STEP3: 將上面sysprog21換成我的github帳號 >$ git clone git@github.com:seizetheday987456321/lab0-c > ==??為何要做這個動作??== E: ssh: could not resolve hostname github.com: Temorary failure in name resolution fatal : could not read from remote repository -> sol: 連接網路, then redo [refer](https://stackoverflow.com/questions/60490170/ssh-could-not-resolve-hostname-github-com-error) E: fatal: destination path 'lab-c' already exists and is not an empty directory. > 朋友說這樣我是從 jserv clone 並不是從我的github clone ...要重新用 $ git remote -v #似乎要在該資料夾才可以使用 ~linux2021/lab0-c origin http://github.com/sysprog21/lab0-c (fetch) origin http://github.com/sysprog21/lab0-c (push) $ cd linux2021 $ rm -r lab0-c #把資料夾移除 ==記得先fork完== $ git clone http://github.com/seizetheday987456321/lab0-c >原本的[開發環境](https://hackmd.io/@sysprog/linux2021-lab0#-%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A)設定可能會對新手造成誤解,變成從老師的地方clone下來 $ git reomote -v #fork 完後從自己的github clone下來,似乎就對了 origin http://github.com/seizetheday987456321/lab-c (fetch) origin http://github.com/seizetheday987456321/lab-c (push) * SETP4: 使用 makefile 命令, [refer1](https://mropengate.blogspot.com/2018/01/makefile.html), [refer2](https://hackmd.io/@sysprog/SySTMXPvl) $ cd lab0-c $ make Git hooks are installed sucessfully. CC qtest.o CC report.o CC console.o CC harness.o CC queue.o CC random.o CC dudect/constant.o CC dudect/fixture.o CC dudect/ttest.o CC linenoise.o LD qtest > 1. cd 是一層一層的 ,就像我的對資料夾雙擊的感覺 > 2. make -> 發現該資料夾的檔案變多了 > make VERBOSE=1 -> 可以看到編譯的細節 > make clean -> 又變少了 > 3. 發現lab0-c 內有一個檔案名為 `Makefile` , 裡面定義了許多命令 e.g. clean clean: #makefile 內的的內容 rm -f $(OBJS) $(deps) *~ qtest /tmp/qtest.* rm -rf .$(DUT_DIR) rm -rf *.dSYM (cd traces; rm -f *~) --- $ make clean #對應的終端機輸入 #輸出結果 rm -f qtest.o report.o console.o harness.o queue.o random.o dudect/constant.o dudect/fixture.o dudect/ttest.o linenoise.o .qtest.o.d .report.o.d .console.o.d .harness.o.d .queue.o.d .random.o.d .dudect/constant.o.d .dudect/fixture.o.d .dudect/ttest.o.d .linenoise.o.d *~ qtest /tmp/qtest.* rm -rf .dudect rm -rf *.dSYM (cd traces; rm -f *~) --- $ ./qtest > 似乎是進到該qtest 執行檔 [作業解說1:10:00](https://www.youtube.com/watch?v=a8GwXtrUc6Q) > $ help >有許多功能 ### Valgrind 實例測試 [影片](https://www.youtube.com/watch?v=a8GwXtrUc6Q) , [資料](https://hackmd.io/@sysprog/linux2021-lab0#Valgrind-%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B) $ cd /tmp/ $ vi case1.c 加入下列程式碼 ```clike= #include <stdlib.h> void func(void) { char *buff = malloc(10); } int main(void) { func(); return 0; } ``` $ :W #存檔 $ :q #離開 $ cat case1.c #cat-> (concatenate)印出來螢幕上 $ gcc -o case1 -g case1.c #進行編譯 $ valgrind -q --leak-check=full ./case1 ==2686== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2686== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==2686== by 0x10865B: func (case1.c:3) ==2686== by 0x10866B: main (case1.c:6) ==2686== ### 牛刀小試 * 嘗試實作 `q->size` -> 新增 `int size` 這個新成員到 `struct queue_t` ```clike typedef struct { list_ele_t *head; /* Linked list of elements */ int size; } queue_t; ``` * 提交修改並 commit $ git commit -m "Change q_size return value to q->size" * 此時若程式縮排不符合格式 Git pre-commit hook 會擋住提交 $ clang-format -i queue.c #執行命令以符合格式 ### 實作 Queue interface * Queue 是一種 ADT (Abstrat data type), 可以把 ADT 的 abstrat 想成是一個簡要的概念. * 通常 ADT 如 Queue , stack 會用一個 interface 把背後的實作隱藏起來 , 讓使用者可以方便使用,不必考慮他的實作細節. * Def : ADT is a data type that is organized in such a way that the specification of th objects and the specification of the operations on the objects . ![](https://i.imgur.com/5WEdDaK.png) refer from [CMU C programming lab](https://www.cs.cmu.edu/afs/cs/academic/class/15213-s20/www/labs/cprogramminglab.pdf) 作業 > 1. 發現 queue.h 只有存定義這些interface的宣告 > 2. queue.c 才會去需要去定義一些實作細節 > -> 為甚麼不都寫在queue.c 裡面?? Note from [CMU Linked list](http://www.cs.cmu.edu/~iliano/courses/18S-CMU-CS122/handouts/10-linkedlist.pdf) > 1. Linked lists are a common alternative to arrays in the implementation of data structures-> easy to insert and delete elements > 2. An item in a linked list consists of a struct containing the data element and a pointer to another linked list. > 3. This definition is an example of a recursive type. >==??發現我們自訂兩個資料型態 list_ele_t、queue_t ,但為甚麼要特別訂一個 queue_t 的結構,如果要記錄 head 的位置,只要另外記錄一個指標位址指向頭,為何要特別因此定義一個結構出來== --- ### 實作下列 functions - [ ] `q_new`: 建立一個空的 queue - [ ] `q_free`: 釋放 queue 所占用的空間 - [ ] `q_insert_tail`:在 tail 端家一個元素( FIFO ) - [ ] `q_insert_head`:在 head 端加一個元素( LIFO ) - [ ] `q_remove_head`:在 tail 端移除一個元素 - [ ] `q_size`:回傳 queue 的元素個數 - [ ] `q_reverse`:將 queue line 反向 - [ ] `q_sort` #### Reconstruct Queue structure ```clike= typedef struct { list_ele_t *head; list_ele_t *tail; #加一個 *tail field 使 q_insert_tail 為 O(1) int size; #加一個 size field 使 q_size 達到 O(1) } queue_t; ``` >1.size_t 是甚麼? >2.為何看到有人用 size_t size 而不是 int size ?? #### q_new - q_new 呼叫後配置一個空的 queue 所需的記憶體空間,若空間不足回傳 Null ```c= queue_t *q_new() { queue_t *q; q = malloc(sizeof(queue_t)); /* TODO: What if malloc returned NULL? */ if (!q) return NULL; q->head = q->tail = NULL; q->size = 0; return q; } ``` >1. queue_t *q_new() , Input 為空, Output 為 queue_t 的型別 >2. sizeof operator 計算 queue_t 型別所需配置的記憶體空間 >3. malloc 函式動態配置記憶體, input 為需要的空間,單位為 byte , Output 為分配的記憶體起始位址 >4. q assign 為空的 queue 的指標, 使用前記得宣告 >5. 記得初始化 head , tail , size 進行操作 :::warning Update : 1. 記得符合 clang-format 規範 2. 忘記 return q 導致 memory leak ::: > 上述的 Update 2. 的原因歸咎於常見的記憶體錯誤 [案例1](https://hackmd.io/@sysprog/linux2021-lab0#-%E9%A0%90%E6%9C%9F%E7%9B%AE%E6%A8%99) ,透過 malloc 一系列操作後,忘記釋放記憶體,且無任何指標指著該記憶體空間.即呼叫 q_new() 後, malloc 一個空間給指標 q ,當呼叫整個函式完畢後,指標 q 會自動釋放 , Refer : [C 語言:變數生命的週期](https://www.youtube.com/watch?v=UPyHGrZMMIc&list=PLY_qIufNHc293YnIjVeEwNDuqGo8y2Emx&index=182) , 但若有 return q , q 的指標變數會 pass 出去給 caller .[color=#0d3efa] #### q_insert_head * q_insert head 呼叫後 insert 一個 element 在此 queue 的 head , return true. * 3種情況 return false , 1. queue 本身是 NULL , 即沒有給定 queue 可以 insert 2. insert element 前會配置記憶體空間給 element 時配置失敗 . 3. 配置記憶體給 char *s 時失敗. * 需配置一個記憶體空間將 char* s 所指的 string 複製過去. ```c= bool q_insert_head(queue_t *q, char *s) { /* Return false if q is NULL */ if (!q) return false; list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); /* Return false if malloc returns NULL for allocate new head*/ if (!newh) return false; /* Return false if allocate memory for strings returns NULL*/ size_t length = strlen(s) + 1; newh->value = malloc(sizeof(char) * length); if (!newh->value) { free(newh); return false; } memcpy(newh->value, s, length); /* if queue is empty , operation is needed for tail */ if (q->size == 0) q->tail = newh; newh->next = q->head; q->head = newh; (q->size)++; return true; } ``` :::warning Update : 1. ERROR: Need to allocate and copy string for new list element -> 題目規定要另外配置一個空間然後把 string 複製過去 ,原本程式是直接指標指過去 newh->value = s; , 另外開一個空間有特別的用意嗎?? 2. Return true 要多考慮一種情況, 即當 queue size == 0 3. strlen() 函式回傳字串長度(char),其中不包含 \0 ,故須+1多 allocate 一個 byte. 4. Error : Memory leak : newh [menleak] ::: > 上述的 Update 4 原因可能歸咎於整個 q_insert_head() 函式呼叫後若配置字串的記憶體失敗, newh 指標便會自動釋放 ,但前面配置成功 newh 的記憶體空間因而無釋放導致的 memory leak.[color=#0d3efa] #### q_inset_tail ```c= bool q_insert_tail(queue_t *q, char *s) { /* Return false if q is NULL */ if (!q) return false; list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); /* Return false if malloc returns NULL for allocate new head*/ if (!newh) return false; /* Return false if allocate memory for strings returns NULL*/ size_t length = strlen(s) + 1; newh->value = malloc(sizeof(char) * length); if (!newh->value) { free(newh); return false; } memcpy(newh->value, s, length); if (!(q->tail)) { q->head = q->tail = newh; (q->head)->next = NULL; } else { (q->tail)->next = newh; newh->next = NULL; q->tail = newh; } (q->size)++; return true; } ``` #### q_remove_head ```c= bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q || (q->size==0)) return false; list_ele_t *rmh = q->head; memcpy(sp,rmh->value,bufsize); if(q->size==1) q->tail=NULL; q->head = rmh->next; free(rmh->value); free(rmh); (q->size)--; return true; } ``` #### q_sort * 題目 : sort elements in ascending order , 直覺來說數字才可以比大小,因此這裡並沒有很明確的說明此 order 是根據甚麼作為比大小的依據. 字串長度? 還是字母大小多寡? 感謝 [jasonmatobo 同學共筆](https://hackmd.io/@jasonmatobo/2021q1_homweork_lab0#q_sort) -> 進入 q_test 自動測試程式,發現 q_test 在執行 sort 命令時,會解譯並呼叫 do_sort 函式, ```c= if (q) { for (list_ele_t *e = q->head; e && --cnt; e = e->next) { /* Ensure each element in ascending order */ /* FIXME: add an option to specify sorting order */ if (strcasecmp(e->value, e->next->value) > 0) { report(1, "ERROR: Not sorted in ascending order"); ok = false; break; } } } ``` -> 可以發現判斷依據式由 strcasecmp() 此函式來判斷 , [Refer](https://linux.die.net/man/3/strcasecmp) int strcasecmp(const char *s1, const char *s2); 此 function 比較 s1 與 s2 字串 , 若 s1 > s2 則 return 大於 0 的 interger ,(忽略字串大小寫) --- * 利用 Merge sort 來達到較好的時間複雜度 O(nlogn) * 利用 divide and conquer 策略來完成,可分為 divide , Conquer , Combine,三個步驟 * 注意要進行的排序對象為 linked list 而不是陣列 -> 做 divide 時找到中間的指標要花點時間 O(n) * 做 Combine 時,利用 dummy node 來幫助進行兩個 linked list 的合併. ```C= void q_sort(queue_t *q) { if(!q||(q->size)==0) return mergesort(q->head,q->size); } void mergesort(list_ele_t *a , size_t size) { if(size>1){ size_t lsize = size / 2; size_t rsize = size - lsize; list_ele_t *rh = Divide(a,lsize); mergesort(a,lsize); mergesort(rh,rsize); a = combine(a,rh); } } list_ele_t *Divide(list_ele_t *cut,size_t step) { for(int i=1 ; i<step; i++){ list_ele_t *cut = cut->next; } list_ele_t *rh = cut->next; cut->next=NULL; return rh; } list_ele_t *combine(list_ele_t *lh, list_ele_t *rh) { list_ele_t Dummy; list_ele_t *tail = &Dummy; Dummy.next = NULL; while(lh&&rh){ if(strcasecmp(lh->value,rh->value)>0){ tail->next=rh; }else{ tail->next=lh; } tail=tail->next; } if(lh){ tail->next=rh; } tail->next=lh; return (Dummy.next); } ``` --- ### `qtest` 命令直譯器實作 執行 q_test 後會見到 `cmd>` 等待使用者輸入 . 打開 qtest.c 檔案 ,可以發現一開始宣告一些會使用到的 function (e.g. `static bool do_insert_head)` , 接著直接看到 `main()`, ```C= main (int argc, char *argv[]) { ... while((c= getopt(...))) srand(...); /* init queue setting , including error handler reservation */ queue_init(); /* Initialize interpreter , for command reservation */ init_cmd(); console_init(); /* Trigger call back function(auto completion) */ linenoiseSetCompletionCallback(completion); ... } ``` * `main (int argc, char *argv[])` , function 的 argc , argv ,可以用來接收 command line 的使用者輸入, 然後把引數傳入 `getopt(argc,argv,"hb:f:l:")` 中 * Callback function : 當使用者鍵入命令 , 以下以 `cmd> new ` 為例 , 直譯器會進行註冊該命令對應的執行 function : `add_cmd("new", do_new, " | Create new queue"); ` , 當直譯器解譯後為 `new` 後, 再間接呼叫 `do_new`, 此函式不是按照執行順序 , 而是等待某個事件發生後再去間接執行稱為 callback function , 呼叫 `do_new` 後再去呼叫我們實作對應的操作 `q_new` . * ##### completion : 使用者按下 tab 鍵進行自動補完 [ `antirez/linenoise` ](https://github.com/antirez/linenoise) , [`Holy`](https://hackmd.io/@Holy/Syc2IgUnv#%E7%A0%94%E8%AE%80%E7%AD%86%E8%A8%98-linenoise) , [`Holychung`](https://hackmd.io/@Holy/2020q3_Homework1_lab0#linenoise) * 啟動時要先註冊 callback function (completion): `linenoiseSetCompletionCallback(completion);` 接著進入 linenoise 一連串 API 呼叫 ...進入 `linenoiseEdit` 中的 `read` 系統呼叫等待使用者於鍵盤輸入 * 輸入之後針對使用者的輸入分析是不是 tab 鍵 , 這邊運用 `linenoiseEdit` 中的 if 判斷 c 是不是等於 9 (c == 9 為 tab ??) , 再依序進入 `completeLine -> completionCallback -> completion ` (前面註冊的時候 : 即註冊 `completionCallback` == `completion`) * 呼叫 completion 函式, 這邊會用 `cmd_list` 去記下輸入過的 command,用 cmd_maybe 去跟現在 buf 的字串比對,如果比對相似就會呼叫 `linenoiseAddCompletion` API 進行補完。 #### signal 應用於 `qtest` 直譯器 參考同學共筆 : [`cjwind` ](https://hackmd.io/@cjwind/r1vWC9tS4?type=view#qtest-運作原理) [`93i7xo2` ](https://hackmd.io/@93i7xo2/HyNGNpXYN#qtest-signal-handling) [`nckuoverload` ](https://hackmd.io/@nckuoverload/2019q3_Homework2_lab0#%E8%A7%A3%E9%87%8B%E8%87%AA%E5%8B%95%E8%A9%95%E5%88%86%E7%B3%BB%E7%B5%B1%E9%81%8B%E4%BD%9C%E7%9A%84%E5%8E%9F%E7%90%86)[`unknowntpo`](https://hackmd.io/@unknowntpo/B1WzhkvKL/%2F%40unknowntpo%2Flab0-c#%E4%BD%9C%E6%A5%AD%E7%AF%84%E6%9C%AC-HackMD) 目的 :自動評分程式 `qtest` 判斷 queue interface 的呼叫是否超時 , 若超時 , 代表函式可能有非預期的問題 , 希望 qtest 可以先跳到對應的 handler 處理 exception 並恢復到還沒執行 queue function 的時候. 運用到的函式 : * signal (SIGALRM , sigalrmhandler) : 這裡 signal 自行註冊自己定義的 handler , 當 process 接受到 SIGALRM 的 signal 跳到對應的 sigalrmhandler 的 handler , 處理 exception. * exception_setup : 在執行 queue interface 的任何動作前 (e.g. new() , insert_head() ...) 執行兩個動作, 分別為設立 non-goto 的恢復點函式 sigsetjmp(env,1) 及設定 Timer 函式 alarm(time_limit) , 用來設定多久超時. * alarm (time_limit) : 呼叫後等待 time_limit 秒後會觸發 `SIGALRM` 這個 signal 作業系統會送這個 signal 到呼叫 alarm 的 process 使之優先處理對應的 handler * sigsetjmp (env ,1) : 設立恢復點的地方 , 此函式會被呼叫兩次 . 第一次在初始設定 , 回傳 0, 即呼叫 exception_setup 後被呼叫到 , 第二次為 setlongjmp() 跳躍回來 , 即非預期狀況產生時 , 恢復到剛剛初始設定的恢復點,故此函式會保存當時的 stack 環境 . 簡單的實驗 : 更改 time_limit 時間 , 或是把 alarm() disabled 掉, 就不會觸發非預期的超時(>1 sec) 了. https://scs.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx#maxResults=250&folderID=%22b96d90ae-9871-4fae-91e2-b1627b43e25e%22 #### RIO 套件 [`KYG-yaya573142`](https://hackmd.io/@KYWeng/S1DPSVSQ8?fbclid=IwAR1fzgqXxhlnYyntG-GZIX2LHl-JeDQxBpUaspL1ZAmDP626WTPHO2SpXDI#%E4%BD%BF%E7%94%A8-antirezlinenoise-%E5%BC%B7%E5%8C%96-qtest-%E5%91%BD%E4%BB%A4%E5%88%97%E5%8A%9F%E8%83%BD) [`akamayu-ouo`](https://hackmd.io/@akamayu-ouo/sysprog21-lab0#%E8%A7%A3%E9%87%8B-select-%E7%B3%BB%E7%B5%B1%E5%91%BC%E5%8F%AB%E5%9C%A8%E6%9C%AC%E7%A8%8B%E5%BC%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F%EF%BC%8C%E4%B8%A6%E5%88%86%E6%9E%90-consolec-%E7%9A%84%E5%AF%A6%E4%BD%9C%EF%BC%8C%E8%AA%AA%E6%98%8E%E5%85%B6%E4%B8%AD%E9%81%8B%E7%94%A8-CSAPP-RIO-%E5%A5%97%E4%BB%B6-%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E8%80%83%E9%87%8F%E9%BB%9E) [`CS:APP`](https://scs.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=f107c2ce-79d5-4529-baeb-2bb495d8c11a) #### Select 系統呼叫 [KYG-yaya573142](https://hackmd.io/@KYWeng/S1DPSVSQ8#%E8%A7%A3%E9%87%8B-select-%E7%B3%BB%E7%B5%B1%E5%91%BC%E5%8F%AB%E5%9C%A8%E6%9C%AC%E7%A8%8B%E5%BC%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F) , [man page](https://man7.org/linux/man-pages/man2/select.2.html) #### 整合 web 功能到 `qtest` 直譯器 [`grb72t3yde`](https://hackmd.io/@grb72t3yde/sysprog_2021q1_lab0#%E5%85%B6%E4%BB%96%E5%AF%A6%E9%A9%97%E4%BB%A5%E5%8F%8A%E5%88%86%E6%9E%90) [`toastcheng`](https://hackmd.io/@toastcheng/w1-lab0#4-%E5%AF%A6%E4%BD%9C-web-server-%E5%8F%8A-coroutine) [`hankluo6`](https://hackmd.io/@hankluo6/2021q1-lab0#%E6%95%B4%E5%90%88-tiny-web-server-%E8%87%B3-lab0) Prerequisite : * 閱讀 `CS:APP` Ch10,11 了解 client server model , Network programming , I/O wrapper 的使用與運作. Problem : * 整合 `web` 命令至 `qtest` 直譯器 , 使得直譯器也可以從瀏覽器接收命令 , 並於瀏覽顯示目前 queue 對應的結果. -> 即瀏覽器扮演 server 端從 url 傳遞參數 ( queue operation request ) 到直譯器內建的 server 功能 , 處理過後得出對應的結果 response to 瀏覽器. * 利用 coroutine 改寫 tiny web server 內部的 fork 的系統呼叫方式 , 來達到直譯器開啟 `web` 功能仍可接受其他命令. 開發順序 : 1 -> 2 -> 3 1. 增加 `web` 功能至 `qtest`直譯器 , 即執行 `qtest` 進入 `cmd>` 命令提示列 , 輸入 `web` , 直譯器接受到命令並解譯後 , 執行 echo server 功能 , 當使用者從瀏覽器輸入命令後會 echo 回來並顯示於瀏覽器上. 2. 更改 echo server 功能, 改為由瀏覽器發出命令後 , server 接受命令執行對應的 queue operation , 之後把對應的結果 response 給瀏覽器. 3. 因為執行 `web` 命令後,會 block 住直譯器再接收下一個命令 , 故須整合 `select` function 使得執行完 `web`後可以同時接受兩個 I/O event.