epoll 陷阱:隧道中的高級負擔

上周提到了 tun/tap 轉發框架的數據通道結構和優化 tun/tap 轉發性能優化,涉及 RingBuffer,packetization 等核心話題。我也給出了一定的數據結構以及處理邏輯,但竟然沒有高尚的 epoll,本文說說它,因為它不適合。

epoll 作為 select,poll 的升級替代,它的優勢在于 “大量描述符場景,主動通知 IO 事件而無需遍歷查找 IO 事件”,這意味著在少量描述符場景,epoll 并無優勢,反而增加復雜性,但復雜性并沒什么大不了的,本文主要強調,epoll 本質上是在串行處理 I 和 O,這導致雙向流量的嚴重性能問題。

萬事皆有因,異步多路復用機制提供的能力是 “將對描述符的 I 和 O 同時復用到同一個線程中”,select,poll,epoll 本質上都是一回事,它們作為一個整體,適合做什么,不適合做什么,要搞清楚,而不能將它們看做不同的東西,因為這樣一來,你很容易陷入 epoll 降維打擊 select,進而萬能無敵的陷阱。

epoll 擅長業務消息的分揀處理,僅分揀消息后交由專門的線程,或直接處理短消息,但對于 tun/tap 等構建的隧道上的持續流量,同一個 socket 的 recv,send 在同一個循環體中會導致半雙工問題,且同一 socket 的 recv 和 send 間,以及不同 socket 的 recv/send 間串行會導致饑餓,這需要引入一個復雜的公平調度機制來解決。總而言之,這種非 “多路復用”問題,epoll 很難應對。

比如,epoll_wait 循環體中處理 POLLIN,POLLOUT 的 if 判斷的位置會直接影響公平性,同時涉及 ET,LT,編碼調度,若非如此,持續的 I 會餓死 O,反之亦然,而對于持續流量,將調度交給系統調度器何樂而不為。

對于 I 和 O,一心不可二用,持續的雙向隧道流量需要的恰恰是解復用,即將同一個描述符的 I 和 O 解復用到不同線程,而不是復用,所以選型時第一要務就應該排除掉 select,poll,epoll,libevent 等異步多路復用技術,而為每一個 socket 簡單地創建兩個線程分別作阻塞 I 和 O 幾乎是唯一選擇,但這由于太簡單而顯得 low,展示不出自己運用復雜技術的能力,進而選擇 epoll 等多路復用的錯誤技術,然后再陷入持續優化的深淵,早干嘛去了。

編程者使用 epoll 處理隧道倒不是都為了炫技,有些也屬于拿著錘子找釘子。受大環境教化對產線工人產出效率的倡導,大多數編程者更熟悉高級框架和高級庫的相關 API,底層的 thread_create 則早就拋到九霄云外去了,從不知或已遺忘了返樸歸真的方法論。

對于高級 API,我的態度還是度量時間尺度,平衡你編碼調試的時間和代碼運行的時間,但前提是你一定要深入理解這個高級 API 的底層,它解決了什么問題,適合做什么,不適合做什么。調用高級 API 肯定增加了程序運行時間,多一個指令就多一個指令的時延,但直接使用底層 API 卻對編程者有極高的要求,否則就會延遲代碼發布和上線時間,同時增加維護和 bugfix 時間,要平衡這兩者。

言歸正傳,既然不使用 epoll,選擇了簡單創建兩個線程,就又涉及線程相關的高級技巧,可謂到處都是坑。

都知道線程比進程更輕量,鞋城更輕量,但為引出線程庫,協程這些高級概念,線程創建,銷毀的管理開銷必須要被詬病,這似乎是引出一個新技術的慣例,于是就在編程者中形成一種新范式,即涉及服務器端的多線程,一定要用線程庫,協程,就像涉及多個 socket 的 IO 一定要用 epoll 一樣。進程,線程,協程的糾纏,與 select,poll,epoll 幾乎無異。

但線程庫,鞋城同樣不適合 tun/tap 隧道。理由和態度依然是度量并平衡時間尺度。

類似 web 服務器 mpm,若采用多線程,為每一個簡單的 request/response 花 80us 創建一個線程并隨后在 100us 后花 30us 銷毀,確實是一筆很昂貴的開銷,線程池被提出解決該問題,資源池化的典型套路,但對于隧道,持續的雙向流哪怕僅存活 1s,創建,銷毀線程不到 200us 的開銷都顯得微不足道,為什么引入復雜性呢,多花幾小時甚至更久的時間調試線程池,再算上維護和 bugfix 時間,創建多少個線程才能償還,而你的代碼在線上的生命周期甚至熬不到那么久。

總結一下,對于隧道,很簡單,不用 epoll,線程池,協程,這些高尚貨,要純性能就別高尚,讓高尚去訴諸軟件工程和項目。

隧道場景遠比服務器場景更簡單,它只是在兩個方向的固定分發,所以只要下面就夠了:

void readtun_thread(void *arg)
{while(1) {block = dequeue(pool);len = read_tun(block);enqueue(tun2socket, block);}
}
void writesocket_thread(void *arg)
{while(1) {block = dequeue(tun2socket);len = write_socket(block); // 后續再 batch 優化enqueue(pool, block);}
}
void readsocket_thread(void *arg) {...}
void writetun_thread(void *arg) {...}

浙江溫州皮鞋濕,下雨進水不會胖。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94051.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94051.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94051.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微前端架構常見框架

1. iframe 這里指的是每個微應用獨立開發部署,通過 iframe 的方式將這些應用嵌入到父應用系統中,幾乎所有微前端的框架最開始都考慮過 iframe,但最后都放棄,或者使用部分功能,原因主要有: url 不同步。瀏覽器刷新 iframe url 狀態丟失、后退前進按鈕無法使用。 UI 不同…

SQL Server更改日志模式:操作指南與最佳實踐!

全文目錄:開篇語**前言****摘要****概述:SQL Server 的日志模式****日志模式的作用****三種日志模式**1. **簡單恢復模式(Simple)**2. **完整恢復模式(Full)**3. **大容量日志恢復模式(Bulk-Log…

git的工作使用中實際經驗

老輸入煩人的密碼 每次我git pull的時候都要叫我輸入三次煩人的密碼,問了deepseek也沒有嘗試成功 出現 enter passphrase for key ‘~/.ssh/id_rsa’ 的原因: 在生成key的時候,沒有注意,不小心設置了密碼, 導致每次提交的時候都會提示要輸入密碼, 也就是上面的提示…

科技賦能,寧夏農業繪就塞上新“豐”景

在賀蘭山的巍峨身影下,在黃河水的溫柔滋養中,寧夏這片古老而神奇的土地,正借助農業科技的磅礴力量,實現從傳統農耕到智慧農業的華麗轉身,奏響一曲科技與自然和諧共生的壯麗樂章。一、數字農業:開啟智慧種植…

imx6ull-驅動開發篇36——Linux 自帶的 LED 燈驅動實驗

在之前的文章里,我們掌握了無設備樹和有設備樹這兩種 platform 驅動的開發方式。但實際上有現成的,Linux 內核的 LED 燈驅動采用 platform 框架,我們只需要按照要求在設備樹文件中添加相應的 LED 節點即可。本講內容,我們就來學習…

深度學習中主流激活函數的數學原理與PyTorch實現綜述

1. 定義與作用什么是激活函數?激活函數有什么用?答:激活函數(Activation Function)是一種添加到人工神經網絡中的函數,旨在幫助網絡學習數據中的復雜模式。類似于人類大腦中基于神經元的模型,激…

Linux高效備份:rsync + inotify實時同步

一、rsync 簡介 rsync(Remote Sync)是 Linux 系統下的數據鏡像備份工具,支持本地復制、遠程同步(通過 SSH 或 rsync 協議),是一個快速、安全、高效的增量備份工具。二、rsync 特性 支持鏡像保存整個目錄樹和…

一種通過模板輸出Docx的方法

起因在2個群里都有網友討論這個問題,俺就寫了一個最簡單的例子。其實,我們經常遇到一些Docx的輸出的需求,“用模板文件進行處理”是最簡單的一個方法,如果想預覽也簡單 DevExpress 、Teleric 都可以,而且也支持 Web 、…

探索 List 的奧秘:自己動手寫一個 STL List?

📖引言大家好!今天我們要一起來揭開 C 中 list 容器的神秘面紗——不是直接用 STL,而是親手實現一個簡化版的 list!🎉你是不是曾經好奇過:list 是怎么做到高效插入和刪除的?🔍迭代器…

mysql占用高內存排查與解決

mysql占用高內存排查-- 查看當前全局內存使用情況(需要啟用 performance_schema) SELECT * FROM sys.memory_global_total; -- 查看總內存使用 SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10; -- 按模塊分類查看內存使用排行memory/perfor…

構建真正自動化知識工作的AI代理

引言:新一代生產力范式的黎明 自動化知識工作的人工智能代理(AI Agent),或稱“智能體”,正迅速從理論構想演變為重塑各行各業生產力的核心引擎。這些AI代理被定義為能夠感知環境、進行自主決策、動態規劃、調用工具并持…

青少年機器人技術(四級)等級考試試卷-實操題(2021年12月)

更多內容和歷年真題請查看網站:【試卷中心 -----> 電子學會 ----> 機器人技術 ----> 四級】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 青少年機器人技術(四級)等級考試試卷-實操題(2021年12月) …

最新短網址源碼,防封。支持直連、跳轉。 會員無廣

最新短網址源碼,防封。支持直連、跳轉。 會員無廣告1.可將長網址自動縮短為短網址,方便記憶和使用。2.短網址默認為臨時有效,可付費升級為永久有效,接入支付后可自動完成,無需人工操作。3.系統支持設置圖片/文字/跳轉頁…

緩存-變更事件捕捉、更新策略、本地緩存和熱key問題

緩存-基礎知識 熟悉計算機基礎的同學們都知道,服務的存儲大多是多層級的,呈現金字塔類型。通常來說本機存儲比通過網絡通信的外部存儲更快(現在也不一定了,因為網絡傳輸速度很快,至少可以比一些過時的本地存儲設備速度…

報表工具DevExpress .NET Reports v25.1新版本亮點:AI驅動的擴展

DevExpress Reporting是.NET Framework下功能完善的報表平臺,它附帶了易于使用的Visual Studio報表設計器和豐富的報表控件集,包括數據透視表、圖表,因此您可以構建無與倫比、信息清晰的報表。 DevExpress Reporting控件日前正式發布了v25.1…

kubernetes中pod的管理及優化

目錄 2 資源管理方式 2.1 命令式對象管理 2.2 資源類型 2.2.1 常用的資源類型 2.2.2 kubectl常見命令操作 2.3 基本命令示例 2.4 運行和調試命令示例 2.5 高級命令示例 3 pod簡介 3.1 創建自主式pod(生產環境不推薦) 3.1.1 優缺點 3.1.2 創建…

解釋一下,Linux,shell,Vmware,Ubuntu,以及Linux命令和shell命令的區別

Linux 操作系統概述Linux 是一種開源的類 Unix 操作系統內核,由 Linus Torvalds 于 1991 年首次發布。作為現代計算的基礎設施之一,它具有以下核心特征:多用戶多任務特性允許多個用戶同時操作系統資源,而模塊化設計使其能夠適應從…

Windows 系統中,添加打印機主要有以下幾種方式

在 Windows 系統中,添加打印機主要有以下幾種方式,我將從最簡單到最復雜為您詳細介紹。 方法一:自動安裝(推薦首選) 這是 Windows 10 和 Windows 11 中最簡單、最現代的方法。系統會自動搜索網絡(包括無線和有線網絡)上可用的打印機并安裝驅動程序。 操作步驟: 進入…

Mixture of Experts Guided by Gaussian Splatters Matters

Mixture of Experts Guided by Gaussian Splatters Matters: A new Approach to Weakly-Supervised Video Anomaly Detection ICCV2025 https://arxiv.org/pdf/2508.06318 https://github.com/snehashismajhi/GS-MoEAbstract 視頻異常檢測(VAD)是一項具有…

SeaTunnel Databend Sink Connector CDC 功能實現詳解

Databend 是一個面向分析型工作負載優化的 OLAP 數據庫,采用列式存儲架構。在處理 CDC(Change Data Capture,變更數據捕獲)場景時,如果直接執行單條的 UPDATE 和 DELETE 操作,會嚴重影響性能,無…