Fastsocket學習筆記之模塊篇
前言
本篇學習Fastsocket內核模塊fastsocket.so
,作為用戶態libfsocket.so
的內核態的支持,處理ioctl
傳遞到/dev/fastsocket
的數據,非常核心和基礎。嗯,還是先翻譯,隨后挾帶些點評進來。
模塊介紹
Fastsocket內核模塊 (fastsocket.ko
) 提供若干特性,并各自具有開啟和關閉等豐富選項可配置。
VFS 優化
CentOS 6.5帶來的內核鎖競爭處處可見,導致無論如何優化TCP/IP網絡堆棧都不能夠帶來很好的性能擴展。比較嚴重鎖競爭例子,inode_lock
和dcache_lock
,針對套接字文件系統sockfs而言,并不是必須。fastsocket通過在VFS初始化結構時提供fastpath快速路徑用以解決此項問題,已經向代號為香草(vanilla)的內核提交了兩處修改:
a209dfc vfs: dont chain pipe/anon/socket on superblock s_inodes list
4b93688 fs: improve scalability of pseudo filesystems
此項修改沒有提供選項可供配置,因此所有fastsocket創建的套接字sockets都會強制經由fastpath傳輸。
內核模塊參數
enable_listen_spawn
fastsocket為每個CPU創建了一個本地socket監聽表(local listen table),應用程序可以決定在一個特定CPU內核上處理某個新的連接,具體就是通過拷貝原始監聽套接字socket,然后插入到本地套接字socket監聽表中。當新建連接在某CPU處理時,系統內核嘗試匹配本地socket監聽表,匹配成功會插入到本地accept隊列中。稍后,CPU會從本地accept隊列中獲取進行處理。
這種方式每一個網絡軟中斷都會有隸屬于自己本地套接字隊列當新的連接進來時可以壓入,每一個進程從本地隊列中彈出連接進行處理。當進程和CPU進行綁定,一旦有網卡接口決定投遞到某個CPU內核上,那么包括硬中斷、軟中斷、系統調用以及用戶進程,都會有這個CPU全程負責。好處就是客戶端請求連接在沒有鎖的競爭環境下分散到各個CPU上被動處理本地連接。
本特性更適合以下情況:
- 盡可能多的網卡Rx接收隊列和CPU核數
- 應用程序工作進程被靜態綁定到每一個CPU上
第一種情況下,RPS可以在網卡接收隊列小于CPU核數時被使用。第二種方案可以滿足兩個方面:
- 應用程序在啟動時自己綁定工作進程和CPU親和性
- 允許fastsocket自動為工作進程綁定CPU親和性
因此,enable_listen_spawn
具有三個值可供配置:
- enable_listen_spawn=0: 徹底禁止
- enable_listen_spawn=1: 啟用,但要求應用程序自己綁定CPU
- enable_listen_spawn=2 (默認值): 啟用此特性,允許fastsocket為每一個工作進程綁定到CPU上
enable_fast_epoll
一旦開啟,需要為文件結構額外添加一字段用以保存文件與epitem的映射關系,這樣可省去在epoll_ctl
方法被調用時從epoll紅黑樹查找epitem的開銷。
雖然此項優化有所修改epoll語義,但帶來了套接字性能提升。開啟的前提是一個套接字只允許添加到一個epoll實例中,但不包括監聽套接字。默認值為true可以適用于絕大多數應用程序,若你的程序不滿足條件就得需要禁用了。
enable_fast_epoll 為布爾型boolean選項:
- enable_fast_epoll=0: 禁用fast-epoll
- enable_fast_epoll=1 (默認值): 開啟fast-epoll
enable_receive_flow_deliver
RFD(Receive Flow Deliver)會把為新建連接分配的CPU ID封裝到其連接的端口號中,而不是隨機選擇新創建的主動連接的源端口進行分配到CPU上。
當應用從活動連接收到數據包RFD解碼時,會從目的地端口上解析出對應的CPU內核ID,繼而轉發給對應的CPU內核。再加上listen_spawn,保證了一個連接CPU處理的完全本地化。
enable_receive_flow是一個布爾型選項:
- enable_receive_flow=0 (默認值): 禁用RFD
- enable_receive_flow=1: 啟用RFD
注意事項:
- 當啟用時,在當前的實現,RFD完全覆蓋RPS策略,并使得RPS無效。若使用RPS,請禁用此特性
- 由于RFD只會對諸如代理應用程序有利,我們建議在Web服務器上禁用此特性
以上,翻譯完畢。
源碼簡單梳理
fastsocket的內核模塊相對路徑為fastsocket/module/,除了README.md外,就是兩個軟連接文件了:
- fastsocket.c ../kernel/net/fastsocket/fastsocket.c 真實環境下不存在這個文件,可能是程序BUG
- fastsocket.h ../kernel/net/fastsocket/fastsocket.h 有對應頭文件存在
換種說法,fastsocket內核模塊真正路徑為fastsocket/kernel/net/fastsocket
,具體文件列表為:
- Kconfig
- Makefile
- fastsocket.h 定義內核模塊所使用到變量和方法
- fastsocket_core.c 負責方法實現,供fastsocket_api.c調用
- fastsocket_api.c 內核模塊加載/卸載等操作,處理前端動態鏈接庫經由ioctl傳遞的數據
fastsocket_api.c實現內核模塊接口,在源碼里面注冊了好多文檔暫時沒有公開的可配置項目:
int enable_fastsocket_debug = 3;
/* Fastsocket feature switches */
int enable_listen_spawn = 2;
int enable_receive_flow_deliver;
int enable_fast_epoll = 1;
int enable_skb_pool;
int enable_rps_framework;
int enable_receive_cpu_selection = 0;
int enable_direct_tcp = 0;
int enable_socket_pool_size = 0;
module_param(enable_fastsocket_debug,int, 0);
module_param(enable_listen_spawn, int, 0);
module_param(enable_receive_flow_deliver, int, 0);
module_param(enable_fast_epoll, int, 0);
module_param(enable_direct_tcp, int, 0);
module_param(enable_skb_pool, int, 0);
module_param(enable_receive_cpu_selection, int, 0);
module_param(enable_socket_pool_size, int, 0);
MODULE_PARM_DESC(enable_fastsocket_debug, " Debug level [Default: 3]" );
MODULE_PARM_DESC(enable_listen_spawn, " Control Listen-Spawn: 0 = Disabled, 1 = Process affinity required, 2 = Autoset process affinity[Default]");
MODULE_PARM_DESC(enable_receive_flow_deliver, " Control Receive-Flow-Deliver: 0 = Disabled[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_fast_epoll, " Control Fast-Epoll: 0 = Disabled, 1 = Enabled[Default]");
MODULE_PARM_DESC(enable_direct_tcp, " Control Direct-TCP: 0 = Disbale[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_skb_pool, " Control Skb-Pool: 0 = Disbale[Default], 1 = Receive skb pool, 2 = Send skb pool, 3 = Both skb pool");
MODULE_PARM_DESC(enable_receive_cpu_selection, " Control RCS: 0 = Disabled[Default], 1 = Enabled");
MODULE_PARM_DESC(enable_socket_pool_size, "Control socket pool size: 0 = Disabled[Default], other are the pool size");
接收用戶態的libfsocket.so通過ioctl傳遞過來的數據,根據命令進行數據分發:
static long fastsocket_ioctl(struct file *filp, unsigned int cmd, unsigned long __user u_arg)
{
struct fsocket_ioctl_arg k_arg;
if (copy_from_user(&k_arg, (struct fsocket_ioctl_arg *)u_arg, sizeof(k_arg))) {
EPRINTK_LIMIT(ERR, "copy ioctl parameter from user space to kernel failed\n");
return -EFAULT;
}
switch (cmd) {
case FSOCKET_IOC_SOCKET:
return fastsocket_socket(&k_arg);
case FSOCKET_IOC_LISTEN:
return fastsocket_listen(&k_arg);
case FSOCKET_IOC_SPAWN_LISTEN:
return fastsocket_spawn_listen(&k_arg);
case FSOCKET_IOC_ACCEPT:
return fastsocket_accept(&k_arg);
case FSOCKET_IOC_CLOSE:
return fastsocket_close(&k_arg);
case FSOCKET_IOC_SHUTDOWN_LISTEN:
return fastsocket_shutdown_listen(&k_arg);
//case FSOCKET_IOC_EPOLL_CTL:
// return fastsocket_epoll_ctl((struct fsocket_ioctl_arg *)arg);
default:
EPRINTK_LIMIT(ERR, "ioctl [%d] operation not support\n", cmd);
break;
}
return -EINVAL;
}
fastsocket/library/libsocket.h
頭文件定義的FSOCKET_IOC_*
操作狀態碼就能夠一一對應的上。 ioctl
傳輸數據從用戶態->內核態,需要經過一次拷貝過程(copy_from_user
),然后根據cmd命令進行功能路由。
libfsocket.so如何與fastsocket內核模塊交互
通過指定的設備通道/dev/fastsocket進行交互:
- fastsocket內核模塊注冊要監聽的通道設備名稱為
/dev/fastsocket
- libfsocket打開
/dev/fastsocket
設備獲得文件句柄,開始ioctl
數據傳遞
小結
簡單梳理了fastsocket內核模塊,但一樣有很多的點沒有涉及,后面可能會在Fastsocket內核篇中再次梳理一下。
posted on 2015-02-03 13:26 nieyong 閱讀(2929) 評論(1) 編輯 收藏 所屬分類: Socket