2023q1 Homework6 (ktcp)

tags: linux2023

contributed by < JoshuaLee0321 >

自我檢查清單

  • 給定的 kecho 已使用 CMWQ,請陳述其優勢和用法

我想要先提到一個在 workqueue.h 中的笑話,在 linux-hwe-5.19-headers-5.19.0-41/include/linux/workqueue.h 的第 343 行,有一個定義了 WQ_MAX_ACTIVE = 512 註解上這樣打: /* I like 512, better ideas? */ 相當的有幽默感。

CMWQ 用法

使用方法其實沒有到很難







G



wq = alloc_workqueue()

wq = alloc_workqueue()



queue_work(wq, job)

queue_work(wq, job)



wq = alloc_workqueue()->queue_work(wq, job)





queue_work(wq, job)->queue_work(wq, job)


while working



destroy_workqueue(wq)

destroy_workqueue(wq)



queue_work(wq, job)->destroy_workqueue(wq)





根據上面的流程圖可以看到,先行宣告一個 struct workqueue_struct *wq,在 alloc_workqueu() 時設定好此 wq 的行為模式,接下來只需要給他工作即可

CMWQ FLAGS

根據 workqueue.h 中有一個 enum 對於各個 FLAG 的定義以及它的名稱,不難猜出要這些參數有甚麼用處。以下來一一介紹:

WQ_UNBOUND
/* 各工作不會被綁定在特定 CPU 上執行 */
WQ_FREEZABLE 
/* 跟電源相關的 flag ,當系統進入冬眠時,有下這個 flag 的 WQ 會完成當前所有 work 並且不啟動新的 work 直到系統解除冬眠 */
WQ_MEM_RECLAIM 
/* 若有這個參數,系統會創建 rescuer thread,當檢測到會產生死結時,出手相助 */
WQ_HIGHPRI
/* 此 wq 有高優先權 */
WQ_CPU_INTENSIVE
/* 此 wq 會特別消耗 CPU ,在 2 中提到: 
 * Runnable CPU intensive work items will not prevent
 * other work items in the same worker-pool from starting
 * execution 
 * 也就是說,他可以算是優先權相對較低的工作 */
 WQ_SYSFS
/* 在 linux file system 中可以看到此 wq */
WQ_POWER_EFFICIENT
/* 當此 work 很吃資源的時候,想省電可以下這個參數 */

參考資料:

  1. CMWQ概述
  2. 為甚麼 WQ_CPU_INTENSIVE 對 unbound 工作隊獵沒有意義
  3. sysfs

CMWQ 優勢

相比於其他自行定義的 queue,如果程式設計者創造出太多 thread,這些 thread 每一個都有一個自己獨立的 PID,有機會導致 kernel space 中的 process 數量太多,而 user space 根本沒有辦法調度任何 process

另外在很多情況下,queue 中的 process 都是需要被異步處理,而通常自己設計的 queue 無法達成這個效果


相比之下,CMWQ明確的劃分了前後端的實作

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 →

讓用戶不需要關心如何分配每個 thread 在任何時間上的需不需要被執行,並且提出 thread pool 機制,讓所有進程都可以存取到共享的 thread

  • 搭配閱讀《Demystifying the Linux CPU Scheduler》第 1 章和第 2 章,描述 CPU 排程器和 workqueue/CMWQ 的互動,應探究相關 Linux 核心原始程式碼

  • 研讀〈Linux 核心設計: RCU 同步機制〉並測試相關 Linux 核心模組以理解其用法

  • 如何測試網頁伺服器的效能,針對多核處理器場景調整

  • 如何利用 Ftrace 找出 khttpd 核心模組的效能瓶頸,該如何設計相關實驗學習。搭配閱讀《Demystifying the Linux CPU Scheduler》第 6 章

  • 解釋 drop-tcp-socket 核心模組運作原理。TIME-WAIT sockets 又是什麼?

讀了許久無法理解,想要請問老師該怎麼去理解這份 kernel module

參考資料

  1. what is net_generic function
  2. char array[0] 1
  3. char array[0] 2

在 khttp 中引入 cmwq

參照 kecho 的程式碼,可以發現為了在核心中有效管理每一個 thread,可以看到在 echo_server.h 中存在

struct echo_service {
    bool is_stopped;
    struct list_head worker;
};

同理,我也在 http_server.h 中新增了同樣的結構方便管理每一個 caller

struct khttp_service {
    bool is_stopped;
    struct list_head worker;
}

再來是修改原本的 http_server_worker.c 在原本的 kecho 當中必須要有幾個制式的流程:

  1. alloc_work()
  2. create_work()
  3. process listhead of daemon
  4. queue_work()
  5. free_work()

所以我們必須要照著上面的流程更改原本的程式碼。

struct workqueue_struct *khttp_wq; ... static int __init khttpd_init(void) { ... khttp_wq = alloc_workqueue(MODULE_NAME, 0, 0); /* workqueue allocated */ http_server = kthread_run(http_server_daemon, &param, KBUILD_MODNAME); ... }

首先更改 main.c 中的全域變數 khttp_wq 當作我們的佇列 (由以上第 1 行紀錄)
再來在第六行的地方初始化剛剛宣告的佇列。

由於我覺得在 http_server.c 又有一個新的結構體,這樣導致我要更改每一個結構體時必須要到不同的檔案中修改,於是我把所有有關結構體的宣告都移動到 http_server.h

... struct http_request { struct socket *socket; enum http_method method; char request_url[128]; int complete; struct list_head worker; struct work_struct http_work; }; ...
  • http_server.c 中的 struct http_request 移動到 http_server.h 並且在第 6, 7 行新增了鏈結串列節點以及 CMWQ 的結構。

接下來更改 http_server.c,首先,先實作 create_work 以及 free_work,兩者皆參考 kecho-echo_server.c 中的對應函式實作

static struct work_struct *create_work(struct socket *sk)
{
    struct http_request *work;
    if( !(work = kmalloc(sizeof(struct http_request), GFP_KERNEL)))
        return NULL;
    
    work->socket = sk;
    /* initalize work */
    INIT_WORK(&work->http_work, http_server_worker);
    /* add work->node into daemon list*/
    list_add(&work->worker, &daemon.head);
    /* return the specific work */
    return &work->http_work;
}
static void free_work(void)
{
    struct http_request *l, *tar;
    list_for_each_entry_safe(tar, l, &daemon.head, worker) {
        kernel_sock_shutdown(tar->socket, SHUT_RDWR);
        flush_work(&tar->http_work);
        sock_release(tar->socket);
        kfree(tar);
    }
}

再來根據 kecho-echo_server.c 中可以發現,其傳遞參數的方法是由 container_of 的方法把 struct work_struct 當作參數放入 khttp_worker 中,所以在 khttp 中也必須要做一樣的事情,並且把相對傳遞參數的方法改掉

/* change void *arg into struct work_struct*/ static void http_server_worker(struct work_struct *work) { struct http_request *worker = container_of(work, struct http_request, http_work); ... realloc: buf = kzalloc(RECV_BUFFER_SIZE, GFP_KERNEL); if (!buf) { pr_err("can't allocate memory!\n"); goto realloc; } http_parser_init(&parser, HTTP_REQUEST); parser.data = &worker->socket; while (!kthread_should_stop()) { int ret = http_server_recv(worker->socket, buf, RECV_BUFFER_SIZE - 1); if (ret <= 0) { if (ret) pr_err("recv error: %d\n", ret); break; } if (!http_parser_execute(&parser, &setting, buf, ret)) continue; if (worker->complete && !http_should_keep_alive(&parser)) break; memset(buf, 0, RECV_BUFFER_SIZE); } kernel_sock_shutdown(worker->socket, SHUT_RDWR); kfree(buf); }

可以看到在第 7 行增加了 realloc 的標籤,由於先前的函數在失敗之後就直接回傳 -1 當作失敗,並且重複使用 thread,在這邊因為沒有辦法回傳,所以我選擇的方法為重複 kzalloc 直到成功為止。另外可以看到第 4 行已經利用 container_of 取出結構體,並且在第 15, 18, 32 行取代成 worker->socket

經過以下的改動,我已經把 CMWQ 實作在 khttp 中了,以下為兩者之間用 hstress.c 的比較

以下數據皆使用 ./htstress localhost:8081 -n 100000 -c 10 來做區別

  • khttpd without CMWQ

  • khttpd with CMWQ

可以看到,在實作了 CMWQ 之後,速度從原本的 4.6 秒降低至 4.01 秒。

提供目錄檔案存取功能,提供基本的 directory listing 功能

利用額外的終端機監控資訊

  • 在實作 khttpd 時,時常不清楚內部回傳值到底如何,尤其當我想要觀察 http_server_worker 時,我總是不知道裡面有哪些參數是可以使用的
    此時我發現可以利用額外的一個終端機不停監控內核中的資訊,使用以下 command
sudo dmesg -wH

另外在修改了 http_server_worker 中的一小部分:

static void http_server_worker(struct work_struct *work)
{
    ...
    while (!kthread_should_stop()) {
    ...
+       pr_info("buf = %s", buf);
    ...
    }
    ...
}

這樣一來就可以隨時得知 buf 中的資訊

題外話 favicon.ico
  • 在把 buf 列印出來後,我發現路徑中一直出現 favicon.ico 這個關鍵字,查了之後才發現許多瀏覽器都會發送此類型的靜態 request

參考資料: favicon.ico

問題

  • 目前怎麼找都沒有找到一個好的方法把 directory 內的東西列出來,想要請問老師有甚麼關鍵字可以搜尋的

在 kernel 中並不存在 process 的概念,如果要做出 directory listing 的功能,必須要給每一個連線者專屬的記憶體空間,讓他可以去訪問當前她想要的東西

  • [ ]