Try   HackMD

in-kernel HTTP server

contributed by 傅鈺融王紹華陳博聖黃靜清

原始程式碼: oiz5201618/kernelhttp

修改自 tianqiu/kernelhttp

執行 kernelhttp

kernelhttp 已正確啟動,可透過網頁瀏覽器存取該伺服器資源,畫面如下:

Network / Host Byte Order

gives the address in network byte order

    /* in_aton */
    s_addr2.sin_addr.s_addr = in_aton("127.0.0.1");  

change host byte order to network byte order

    /* 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

程式解析

:warning: 參考以下得知背景知識: (均有解說錄影)

  1. CS:APP 第 11 章重點提示和練習
  2. server-framework

前言

這個程式架構是利用 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( ) 的實作

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

在這個架構中,需要資料複製的成本。

Kernel web server 透過模組直接掛載在 kernel space 之中

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • 圖中 client 直接存取 kernel 中的 server socket,因此不需要有資料複製的成本!
  • 要注意的是,此處的 server request handler 是不局限於 user space 的,也可以將處理 request 的工作放在 kernel 之中。
  • 邊 kernel web 中使用到了 netlink 的系統呼叫,是 kernel 與 user 直接的溝通。

程式碼

  • server.c

server.c 是用來定義 server.ko 這個模組行為的實作,因此先要有 Linux 核心模組的認知

參見鳥哥解說

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.kourl.py 透過 TCP/IP 連線做為 Linux 核心和 user-space 間的訊息交換機制(listen for server's connection),所以url.py 本身也是個 web server。

  • server_npy.c

server_npy 版本中,我們將 python 處理的部份抽離架構,但這樣我們還要補上處理 request 的部份。

這個部份我們首先使用與 server-framework 相同的處理方式,收到 request 後我們將固定的 response (http header及hello world)回應給 client,如下所示:

        ...
        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 回應的緩衝區內,這樣也能呈現出上面測試的結果。

執行流程圖

修改前架構

修改後架構

可在上圖發現原本架構是讓 clients 透過 port:8888與 server 進行連線,但在處理 requests 上,模組額外建立連線(連線到本機位址的 port:9999)與 url.py 來進行溝通,但額外建立連線是有成本的,因此我們試著將架構修改。

效能分析

效能(修改前)

    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
  • The Design of kHTTPd
  • Linux 早期核心 khttpd 伺服器 —— 策略污染機制
    • 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 伺服器,核心模組仍可派上用場

待整理