# 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 ~
>
 [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 .

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.