---
tags: LINUX KERNEL, LKI
---
# in-kernel HTTP server
contributed by [傅鈺融]()、[王紹華](https://github.com/oiz5201618)、[陳博聖]()、[黃靜清](https://github.com/workfunction)
原始程式碼: [oiz5201618/kernelhttp](https://github.com/oiz5201618/kernelhttp)
> 修改自 [tianqiu/kernelhttp](https://github.com/tianqiu/kernelhttp)
## 執行 kernelhttp
當 [kernelhttp](https://github.com/oiz5201618/kernelhttp) 已正確啟動,可透過網頁瀏覽器存取該伺服器資源,畫面如下:
![](https://i.imgur.com/jOOJMKE.png)
## Network / Host Byte Order
gives the address in network byte order
```cpp
/* in_aton */
s_addr2.sin_addr.s_addr = in_aton("127.0.0.1");
```
change host byte order to network byte order
```cpp
/* htons:host to network short */
s_addr2.sin_port = htons(portnum2);
```
Network byte order 其實就是 Big-Endian,因為這個順序與網路類型順序一樣。而 Host Byte Order 指電腦硬體架構所依循的 byte order ,所以會依 CPU 而有所不同。
因此在使用 socket 時,必須注意自己電腦的 IP 跟想要聆聽的 port 必須再同樣的 byte order 之下,也就是 Network Byte Order
## 程式解析
:::success
:warning: 參考以下得知背景知識: (均有解說錄影)
1. [CS:APP 第 11 章重點提示和練習](/@sysprog/CSAPP-ch11)
2. [server-framework](https://hackmd.io/@ktvexe/B1s8hX1yg)
:::
### 前言
這個程式架構是利用 server.c (透過 port 8888) 來聆聽 clients 的 request,當接收到後便將 request 轉發 (forward) 給 url.py (透過 port 9999),當 url.py 處理完並回傳給 server.c ,server.c 在將 response 回應給 clients。
在整體而言,這樣實作的效能是受到質疑的,當我透過 TCP/IP 接收到要求時,再次利用 TCP/IP 來進行「電腦內部的資料交換」,連線的成本是相當高的!(在效能分析可看出其中差異),也因此我們將實作修改成不透過外部,而是在內部處理 request 並回傳 response 給 clients。
修改過程中,我們也將探討 Linux 核心原始碼 workqueue 如何實作。
**User space 透過系統呼叫 socket( ) 的實作**:
![](https://hackpad-attachments.s3.amazonaws.com/embedded2016.hackpad.com_Vbmxrh9mRPj_p.572037_1463383801648_%E6%9C%AA%E5%91%BD%E5%90%8D.png)
在這個架構中,需要資料複製的成本。
**Kernel web server 透過模組直接掛載在 kernel space 之中**:
![](https://hackpad-attachments.s3.amazonaws.com/embedded2016.hackpad.com_Vbmxrh9mRPj_p.572037_1463384021950_%E6%9C%AA%E5%91%BD%E5%90%8D.png)
* 圖中 client 直接存取 kernel 中的 server socket,因此不需要有資料複製的成本!
* 要注意的是,此處的 server request handler 是不局限於 user space 的,也可以將處理 request 的工作放在 kernel 之中。
* 邊 kernel web 中使用到了 netlink 的系統呼叫,是 kernel 與 user 直接的溝通。
### 程式碼
- [ ] `server.c`
server.c 是用來定義 server.ko 這個模組行為的實作,因此先要有 Linux 核心模組的認知
> 參見[鳥哥解說](https://linux.vbird.org/linux_basic/centos7/0510osloader.php#kernel_look)
`server.c` 中在工作排程方面是透過 Linux 核心中 workqueue.h 提供的 API 來實作;連線方面則是透過 net.h 提供的 API 來建立 socket
- [ ] `url.py`
`url.py`是用來處理 request 並產生相對應的 response,為了能提供適當(符合 request )的 response,裡面定義了一系列的函數去解析及處理 http request 。
產生 response 的方法則是呼叫另一個 python script `dealdir`在 current/working directory 中尋找相對應的檔案(html、jpg)作為 response。
由於 `server.ko` 與 `url.py` 透過 TCP/IP 連線做為 Linux 核心和 user-space 間的訊息交換機制(listen for server's connection),所以`url.py` 本身也是個 web server。
- [ ] `server_npy.c`
在 `server_npy` 版本中,我們將 python 處理的部份抽離架構,但這樣我們還要補上處理 request 的部份。
這個部份我們首先使用與 [server-framework](https://github.com/ktvexe/server-framework) 相同的處理方式,收到 request 後我們將固定的 response (http header及hello world)回應給 client,如下所示:
```cpp
...
snprintf(response_buf, 1024,
"HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n"
"Connection: keep-alive\r\n"
"Keep-Alive: timeout=2\r\n"
"\r\n"
"Hello World!");
...
vec_client.iov_base = response_buf;
...
ret = kernel_sendmsg(wsdata->client, &msg_client, &vec_client, 1, len);
```
另一種處理方式則是觀察 `url.py` 回傳的 responses ,然後寫死在模組裡要給 client 回應的緩衝區內,這樣也能呈現出上面測試的結果。
## 執行流程圖
### 修改前架構
![](https://paper-hackpad-attachments.dropbox.com/embedded2016.hackpad.com_QcyEen3Ar39_p.577296_1463247520029_skm.png?fit=max&w=882)
### 修改後架構
可在上圖發現原本架構是讓 clients 透過 port:8888與 server 進行連線,但在處理 requests 上,模組額外建立連線(連線到本機位址的 port:9999)與 `url.py` 來進行溝通,但額外建立連線是有成本的,因此我們試著將架構修改。
![](https://paper-hackpad-attachments.dropbox.com/embedded2016.hackpad.com_QcyEen3Ar39_p.577296_1463248652730_smkv2.png?fit=max&w=882)
## 效能分析
### 效能(修改前)
```
Server Software: erver
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 1175 bytes
Concurrency Level: 32
Time taken for tests: 0.007 seconds
Complete requests: 32
Failed requests: 25
(Connect: 0, Receive: 0, Length: 25, Exceptions: 0)
Total transferred: 463514 bytes
HTML transferred: 459770 bytes
Requests per second: 4641.04 [#/sec] (mean)
Time per request: 6.895 [ms] (mean)
Time per request: 0.215 [ms] (mean, across all concurrent requests)
Transfer rate: 65649.08 [Kbytes/sec] received
```
### 效能(修改後)
```
Server Software: erver
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 988 bytes
Concurrency Level: 32
Time taken for tests: 0.001 seconds
Complete requests: 32
Failed requests: 0
Total transferred: 35360 bytes
HTML transferred: 31616 bytes
Requests per second: 50314.47 [#/sec] (mean)
Time per request: 0.636 [ms] (mean)
Time per request: 0.020 [ms] (mean, across all concurrent requests)
Transfer rate: 54294.42 [Kbytes/sec] received
```
### user space 效能
```
Server Software:
Server Hostname: localhost
Server Port: 8080
Document Path: /
Document Length: 13 bytes
Concurrency Level: 32
Time taken for tests: 0.002 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 9900 bytes
HTML transferred: 1300 bytes
Requests per second: 53304.90 [#/sec] (mean)
Time per request: 0.600 [ms] (mean)
Time per request: 0.019 [ms] (mean, across all concurrent requests)
Transfer rate: 5153.50 [Kbytes/sec] received
```
reactor 會再執行一次 srv_cycle_core 時,確認狀態後一次關閉。也因此當有任務完全後,卻要等待其他任務完成才能再次獲得新的 reactor,也因此只要在任務執行結束後將 reactor 做關閉,就可以更加的有效率!
## TUX
- [TUX: Ingo Molnar Responds](https://news.slashdot.org/story/00/07/20/1440204/answers-from-planet-tux-ingo-molnar-responds)
- [The Design of kHTTPd](https://www.linux.it/~rubini/docs/khttpd/khttpd.html)
- [Linux 早期核心 khttpd 伺服器 —— 策略污染機制](https://blog.51cto.com/dog250/1274021)
- Linux 核心差一點就走進 Microsoft Windows 的「為需求而升級」的發展道路從而偏離原先 Linux 核心「只提供機制不提供策略」的道路
- Linux 2.4 時代,核心提供一個名為 `khttpd` 的核心內部的 web 伺服器 (in-kernel httpd)。當時 Linux 的效能不夠好,且無法有效利用 SMP 的優勢、thread 的實現還是很拙劣 (不是 NPTL),很多方面沒有達到 POSIX 的高效能指標,因此當時的開發者就鑽進瞭解決性能瓶頸的牛角尖上
- 有鑑於 Apache 伺服器多半在 Linux 上運作,且 web 伺服器是如此普遍也重要,因此效能瓶頸往往是 Apache 本身,因此面對這個如此「特化」又不得不理會的需求,開發者們只好就事論事地將 web 伺服器單獨加速,也就是在核心內部中實做一個 web 加速器
- 如果按照這條路走下去,Linux 還會把圖形處理搬到核心內部,一如 Windows NT 所為
- 不過 Linux 2.6 以來,核心發展重新找到自己的方向,沒必要透過特化需求去克服局部的效能瓶頸
- Linux-2.6 核心的發展策略是基於整體思維,像是新的排程演算法 (`O(1)` scheduler 和後續的 CFS),又像是 linux-2.6.17 中的 splice 和 tee 系統呼叫,都讓 `khttpd` 沒有存在的必要
- 不過作為 [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) 伺服器,核心模組仍可派上用場
## 待整理
* [Homework8: Server-framework](https://hackmd.io/@YOeog-b6SN6o9HMwnuC3GA/rkT3Zt5eG)