--- 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)