# 2019q1 Homework5 (daemon)
contributed by < `jeffcarl67` >
## 環境
* 4.19.36-1-MANJARO
* gcc (GCC) 8.3.0
## 缺點
輸入時 fastecho 會讀取到多餘的字元,例如在我的電腦上輸入 a 時,在 `dmesg` 中顯示
```
[ 6526.225454] fastecho: get request = a
\xfd\xf3O\xd9▒\xe0\xd1&%tU
```
可以看到字串 `\xfd\xf3O\xd9▒\xe0\xd1&%tU` 並非由使用者輸入,再看到輸出語句
```c=
printk(MODULE_NAME ": send request = %s\n", buf);
```
由於參數 `%s` 會使函式 `printk` 持續輸出字元直到遇到 `'\0'` 為止,因此猜測多餘字元可能是在
```c=
buf = kmalloc(BUF_SIZE, GFP_KERNEL);
```
取得的記憶體中殘留的資料,解決的思路參看[kernel_recvmsg](https://www.kernel.org/doc/htmldocs/networking/API-kernel-recvmsg.html) 有以下描述
> The returned value is the total number of bytes received, or an error.
由於函式 `kernel_recvmsg` 在未出錯時會返回讀取的 byte 數量,因此加入如下敘述:
```c=
if (length > 0 && length < size)
buf[length] = '\0';
```
於每次讀取的字串後加入 `'\0'` 即可避免函式 `printk` 輸出多餘字元,然而此時會出現 `fastecho` 回傳的字元被下一次的輸入覆蓋的現象,推測原因在於 `fastecho` 回傳的字元中不包含換行符號 `'\n'`,在呼叫函式 `send_request` 前於緩衝區尾端加入一個 `'\n'` 字元可以修正輸出被覆蓋的問題,如下:
```c=
int length;
...
while (!kthread_should_stop()) {
...
length = strlen(buf);
if (length < BUF_SIZE - 1) {
buf[length] = '\n';
buf[length + 1] = '\0';
}
res = send_request(sock, buf, strlen(buf));
...
```
## 測試
### 訊息回傳
利用單一執行緒的 [python 腳本](https://github.com/jeffcarl67/kecho/blob/master/echo_test.py)測試 `fastecho` 在只有一個 client 時在不同請求次數下所消耗的時間,所得結果如下:

可以發現 `fastecho` 的表現十分穩定,消耗的時間與次數成正相關,而查看 `dmesg` 會看到 `fastecho` 的行為大致符合預期
### client 連接
測試 `fastecho` 在有大量 client 的情況下處理連線的效能,在這個實驗中每個 client 只會傳送一次並擷取一次訊息就關閉連線,使用的工具修改自 [htstress](https://github.com/arut/htstress),修改後的版本在此 [htstress.c](https://github.com/jeffcarl67/kecho/blob/master/htstress.c)
由於 `htstress` 原先設計用來測試 http server 的效能,通常當 http server 傳送完資料後會關閉連線,而 `fastecho` 並不會主動關閉連線,導致使用原本版本的 `htsress` 時會發現 `htsress` 與 `fastecho` 在完成第一次資料傳送後同時阻塞等待對方傳送資料,因此修改 `htsress` 使其在完成一次 echo 後就關閉連線,奇怪的是在原本版本中會觀察到在完成第一次 echo 後函式 `kernel_sendmsg` 會回傳 0 導致 kernel thread 結束,目前還未找到原因
以下為 `hstress` 的命令參數:
```
Usage: htstress [options] [http://]hostname[:port]/path
Options:
-n, --number total number of requests (0 for inifinite, Ctrl-C to abort)
-c, --concurrency number of concurrent connections
-t, --threads number of threads (set this to the number of CPU cores)
-d, --debug debug HTTP response
--help display this message
```
使用命令 `./htstress -n 10000 -c 1 -t 1 localhost:12345` 執行後得到如下結果
```
requests: 10000
good requests: 10000 [100%]
bad requests: 0 [0%]
seconds: 0.721
requests/sec: 13870.280
```
而使用命令 `./htstress -n 10000 -c 1 -t 4 localhost:12345` 執行後得到如下結果
```
requests: 10000
good requests: 10000 [100%]
bad requests: 0 [0%]
seconds: 1.675
requests/sec: 5971.672
```
可以發現在多執行緒下多個 client 同時連線時效能反而下降了,可見 `fastecho` 對多執行緒下的情況處理能力依然不夠完善
## workqueue
參考 [kernelhttp](https://github.com/oiz5201618/kernelhttp) 以類似的方式加入 `workqueue`,之後以命令 `./htstress -n 10000 -c 10 -t 4 localhost:12345` 測試使用 `workqueue` 後效能是否有變化,結果如下
```
requests: 10000
good requests: 10000 [100%]
bad requests: 0 [0%]
seconds: 0.470
requests/sec: 21296.896
```
可以發現改為使用 `workqueue` 後效能顯著上升,推測原因為 `workqueue` 能更好的利用多核心
## kernelhttp
修改 `server_npy.c` 將原本的函式 `int myserver(void)` 變為
`int http_server_daemon(void *arg)` 並使其在 `fastecho` 載入後成為 kernel thread,執行的功能與原先相同,修改後的版本見 [server_npy.c](https://github.com/jeffcarl67/kecho/blob/master/server_npy.c)
使用 [ApacheBench](https://httpd.apache.org/docs/2.4/programs/ab.html) 測試 `fastecho` 中 `kernelhttp` 的效能,以參數 `ab -n 10000 -c 10 http://localhost:8888/` 得到如下結果:
```
Server Software:
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 12 bytes
Concurrency Level: 10
Time taken for tests: 0.509 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 980000 bytes
HTML transferred: 120000 bytes
Requests per second: 19662.20 [#/sec] (mean)
Time per request: 0.509 [ms] (mean)
Time per request: 0.051 [ms] (mean, across all concurrent requests)
Transfer rate: 1881.73 [Kbytes/sec] received
```