偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

TCP/IP協(xié)議棧到底是內(nèi)核態(tài)好還是用戶態(tài)好?

網(wǎng)絡(luò) 通信技術(shù)
Linux內(nèi)核協(xié)議棧的收包處理和TCP新建連接的性能,同時還了解了一下騰訊的F-Stack。這里指明,我的mtcp使用的是netmap作為底層支撐,而不是DPDK。

[[420557]]

本文轉(zhuǎn)載自微信公眾號「極客重生」,作者dog250。轉(zhuǎn)載本文請聯(lián)系極客重生公眾號。

“TCP/IP協(xié)議棧到底是內(nèi)核態(tài)的好還是用戶態(tài)的好?”

問題的根源在于,干嘛非要這么刻意地去區(qū)分什么內(nèi)核態(tài)和用戶態(tài)。

引子

為了不讓本文成為干巴巴的說教,在文章開頭,我以一個實例分析開始。

最近一段時間,我?guī)缀趺刻焐钜苟荚谧鲆患拢瑢Ρ萴tcp,Linux內(nèi)核協(xié)議棧的收包處理和TCP新建連接的性能,同時還了解了一下騰訊的F-Stack。這里指明,我的mtcp使用的是netmap作為底層支撐,而不是DPDK。

測試過程中,我確認了Linux內(nèi)核協(xié)議棧的scalable問題并且確認了用戶態(tài)協(xié)議棧是如何解決這個問題的。然而這并沒有讓我得出用戶態(tài)協(xié)議棧就一定比內(nèi)核態(tài)協(xié)議棧好這么一個明確的結(jié)論。具體怎么講呢?先來看一張圖,這張圖大致描述了我的測試結(jié)論:

可以看出,Linux內(nèi)核協(xié)議棧存在嚴重的scalable問題(可伸縮性),雖然我們看到用戶態(tài)協(xié)議棧性能也并非完美地隨著CPU核數(shù)的增加而線性擴展,但已經(jīng)好太多了。看到這個結(jié)論,我們不禁要問,Why?兩個問題:

  • 為什么內(nèi)核協(xié)議棧PPS曲線呈現(xiàn)嚴重上凸?
  • 為什么內(nèi)核協(xié)議棧的CPS(TCP每秒新建連接數(shù))隨著CPU核數(shù)的增加幾乎沒有什么變化?

第一個問題好回答,就像《人月神話》里說的一樣,任何事情都不能完美線性擴展,因為溝通需要成本。好吧,當我巧妙繞開第一個問題后,我不得不深度解析第二個問題。

我們知道,Linux內(nèi)核協(xié)議棧會將所有的Listener socket和已經(jīng)建立連接的establish socket分別鏈接到兩個全局的hash表中,這意味著每一個CPU核都有可能操作這兩張hash表,作為搶占式SMP內(nèi)核,Linux處理TCP新建連接時加鎖是必須的。

好在如今的新內(nèi)核的鎖粒度已經(jīng)細化到了hash slot,這大大提升了性能,然而面對hash到同一個slot的TCP syn請求來講,還是歇菜!

特別嚴重的是,如果用戶態(tài)服務器僅僅偵聽一個Nginx 80端口,那么這個機制就相當于一個全局的內(nèi)核大鎖!對于Listener的slot鎖,那是為多個Listener而優(yōu)化的(最多INET_LHTABLE_SIZE個bucket,即32個),對于僅有一個Listener的新建連接而言,不會起到任何作用。但是這個只是在頻繁啟停服務+reuseport的時候才會發(fā)生,無關(guān)我們描述的場景

對于TCP新建連接測試,很顯然要頻繁操作那張establish hash表,握手完成后加鎖插入hash表,連接銷毀時加鎖從hash表刪除!

問題已經(jīng)描述清楚了,要揭示答案了。

對于TCP CPS測試而言,會有頻繁的連接創(chuàng)建和鏈接銷毀的過程在執(zhí)行,映射到代碼,那就是inet_hash和inet_unhash兩個函數(shù)會頻繁執(zhí)行,我們看一下unhash:

  1. void inet_unhash(struct sock *sk) 
  2. struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; 
  3.     spinlock_t *lock; 
  4. int done; 
  5.  
  6. if (sk_unhashed(sk)) 
  7. return
  8.  
  9. if (sk->sk_state == TCP_LISTEN)  
  10. lock = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)].lock; 
  11. else // 多核SMP且高壓力下,難免會有多個socket被hash到同一個slot 
  12. lock = inet_ehash_lockp(hashinfo, sk->sk_hash); 
  13.  
  14.  
  15.     spin_lock_bh(lock); // 這里是問題的根源 
  16.     done =__sk_nulls_del_node_init_rcu(sk); 
  17. if (done) 
  18.         sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 
  19.     spin_unlock_bh(lock); 

關(guān)于hash的過程就不贅述了,同樣會有一個spinlock的串行化過程。

這似乎解釋了為什么內(nèi)核協(xié)議棧的CPS如此之低,但依然沒有解釋為什么內(nèi)核協(xié)議棧的CPS如此不scalable,換句話說,為何其曲線上凸。

從曲線上看,虛線的斜率隨著CPU核數(shù)的增加而減小。而曲線的斜率和溝通成本是負相關(guān)的。這里的溝通成本就是沖突后的自旋!

不求完全定量化分析,我們只需要證明另外一件事即可,那就是隨著CPU核數(shù)的增加,slot沖突將會加劇,從而導致spinlock更加頻繁,即CPU核心和spinlock頻度是正相關(guān)的!!

這是顯然的,且這很容易理解。如果我們的hash函數(shù)是完美的,那么每一次hash都是不偏不倚的,最終的hash bucket分布將是概率均勻的。CPU核數(shù)的增加并不會改變這個結(jié)論:

結(jié)論是,CPU核數(shù)的增加,只會加劇沖突,因此CPU核數(shù)越多,spinlock頻度就越高,明顯地二者正相關(guān)!spinlock隨著CPU核數(shù)的增加而增加,CPU核數(shù)增加的收益被同樣增加的spinlock成本完美抵消,所以說,隨著CPU核數(shù)的增加,CPS幾乎不會變化。

好了,這就是內(nèi)核協(xié)議棧的缺陷,它能不能改進取決于你的決心,以上的描述沒有任何細節(jié)證明在內(nèi)核態(tài)實現(xiàn)協(xié)議棧是不好的,相反,它只是證明了Linux內(nèi)核如此這般的實現(xiàn)方法存在問題,僅此而已。

進入真正的形而上論述之前,扯點題外話。

首先推薦一個騰訊云的F-Stack,其github地址為:

https://github.com/F-Stack/f-stack

我們能從以下的鏈接看到它的全貌:

http://www.f-stack.org/

之所以推薦這個,是因為它是一個我們大家都能看得見摸得著便于深度交流的中文社區(qū)的用戶態(tài)協(xié)議棧實現(xiàn),并且是開源的。沒有任何私心,但真心感覺不錯。

其次聊一下第一代以太網(wǎng)的CSMA/CD。

這玩意兒相信幾乎所有搞IT的聽說過,一般學習網(wǎng)絡(luò)的第一節(jié)課或者第二節(jié)課,老師都會提到這個,但也就僅僅這兩節(jié)課,后面就再也不提了。我們都知道,如今的交換式以太網(wǎng)早就不用CSMA/CD那一套了…但無論如何,返璞歸真總是會讓人有所收獲。

CSMA/CD是一個被動的搶占式協(xié)議,基于NAK而不是ACK,它具有典型的不聯(lián)系就是沒事,出事了我會聯(lián)系你的90后特征。這個場景一般在爸媽送孩子上車的時候常見,“到了打電話啊”…但一般孩子們都會說,“有事再打電話,沒事就不打了”,這種行為就是NAK。而ACK則完全相反。CSMA/CD就是NAK協(xié)議,有沖突就停等,沒沖突繼續(xù)發(fā),但凡這種NAK協(xié)議都有一個致命的問題,即scalable問題,原因參見我上面關(guān)于hash概率均勻的論述。對于第一代以太網(wǎng)而言,如果網(wǎng)絡(luò)上有越多的節(jié)點,就會出現(xiàn)越多的沖突,對于單個節(jié)點而言,其通信質(zhì)量就會下降,這就是為什么第一代基于CSMA/CD協(xié)議的以太網(wǎng)的用戶數(shù)/性能曲線呈現(xiàn)上凸趨勢的原因。

現(xiàn)在回到Linux內(nèi)核協(xié)議棧CPS問題的spinlock,其也是一個NAK協(xié)議,沖突了就等,直到等到,這是一種完全消極的被動應對方式。注定會失去scalable!!

Linux內(nèi)核里充斥著大量的這種被動邏輯,但卻沒有辦法去優(yōu)化它們,因為一開始它們就這么存在著。典型的場景就是,TCP短鏈接加上nf_conntrack。二者全部都需要操作全局的spinlock,嗚呼,悲哀!

最后說一下HTTP。搞底層協(xié)議的一般不會太關(guān)注HTTP,但是HTTP請求頭里的Connection字段會對性能產(chǎn)生巨大的影響,如果你設(shè)置為close,那就意味著服務器在完成任務后就斷開TCP連接,如果你設(shè)置為Keep-Alive,就意味著你可以在同一個TCP連接中請求多個HTTP。但這看起來也不是很有用。作為一個瀏覽器客戶端,誰管你服務器能撐得住多少CPS啊!就算我個人將Connection設(shè)置為Keep-Alive,別人不從,又能把他們怎樣?

ACK和NAK

一般認為NAK協(xié)議可以最大限度的節(jié)約空間,但是卻浪費了時間,然而在帶寬資源非常緊缺的TCP協(xié)議誕生伊始,多發(fā)一個字節(jié)都嫌多,為什么卻選擇了ACK而不是NAK?

答案正是空間換取scalable。后來的事實證明,TCP選擇了ACK,這是一個正確的選擇。

NAK表明你必須被動地等待壞消息,沒有消息就是好消息,但是要等多久呢?不得而知。而ACK的主動報送則可以讓你規(guī)劃下一步的動作。

有沒有深夜喝完酒和朋友分開的經(jīng)歷。我酒量還可以,所以一般都是擔心朋友路上出點什么事情,一般我都會讓朋友到家后發(fā)個微信表明自己到家了,這樣我收到他的信息后就能安然入睡了。如果這個時候用NAK協(xié)議會怎樣?對于一個本來就不可信的信道而言,就算他給我打了求救電話也有可能接不通,我要等到什么時候才能確保朋友已經(jīng)安全到家?

TCP的ACK機制作為時鐘驅(qū)動其發(fā)送引擎源源不斷地發(fā)送數(shù)據(jù),最終可以適應各種網(wǎng)絡(luò)環(huán)境,也正為如此,30多年前的TCP到現(xiàn)在還依然安好(PS:這里并不能讓我釋懷,因為我討厭TCP。我在這里說TCP的好話,僅僅是因為它選擇了ACK而不是NAK這件事是正確的,僅此而已)。

鄙視鏈

把內(nèi)核態(tài)和用戶態(tài)做了一個界限分明的區(qū)分,于是一條鄙視鏈就形成了,或者說反過來就是是一條膜拜鏈。在內(nèi)核態(tài)寫代碼的鄙視寫應用程序的,寫用戶態(tài)代碼的膜拜搞內(nèi)核的(然后把Java和C都扯進來,搞C的鄙視搞Java的?)。

先不談鄙視鏈,也不談膜拜鏈。只要區(qū)分了內(nèi)核態(tài)和用戶態(tài),那么想要實現(xiàn)一個功能的時候,就必然面臨一個選擇,即在什么態(tài)實現(xiàn)它。緊接著而來的就是一場論戰(zhàn),隨便舉幾個例子。

  • Linux 2.4內(nèi)核中有個小型的WEB服務器,結(jié)果被鄙視了
  • 現(xiàn)如今Facebook搞了個KTLS,旨在把SSL過程放在內(nèi)核里以支持高性能HTTPS
  • 我自己把OpenVPN數(shù)據(jù)通道移植進內(nèi)核,見人就拿這事跟人家炫耀
  • 騰訊F-Stack把BSD協(xié)議棧嫁接在用戶態(tài),美其名曰高效,靈活
  • mtcp貌似也做了同樣的事
  • Telira的銷售不厭其煩告訴我用戶態(tài)的DPI是多么高效
  • 微軟的很多網(wǎng)絡(luò)服務都實現(xiàn)在內(nèi)核態(tài)

無論怎么搞,套路基本就是原來在內(nèi)核態(tài)實現(xiàn)的,現(xiàn)在移到用戶態(tài),原來在用戶態(tài)實現(xiàn)的,現(xiàn)在移到內(nèi)核態(tài),就這么搞來搞去,其中有的很成功,有的就很失敗,所謂的成功是因為這個移植解決了特定場景下的特定問題,比如用戶態(tài)協(xié)議棧的mmap+Polling模式就解決了協(xié)議棧收包PPS吞吐率低的問題,然而很多移植都失敗了,這些失敗的案例很多都是為了移植而移植,故意搗騰的。

請注意,不要被什么用戶態(tài)協(xié)議棧所誤導,世界上沒有萬金油!

正文

很多人混淆了原因和結(jié)果,正如混淆目標和手段一樣常見。

我們都知道,Linux內(nèi)核協(xié)議棧收包吞吐低,然而當你問起Linux內(nèi)核協(xié)議棧為什么這樣時,回答大多是“切換開銷大”,“內(nèi)存拷貝太多”,“cache miss太高”,“中斷太頻繁”…但是注意,作為使用協(xié)議棧的業(yè)務方?jīng)]人會關(guān)注這些,實際上這些都是原因而不是結(jié)果,業(yè)務關(guān)注的就是收包吞吐低這個事實,為什么吞吐低這正是內(nèi)核工程人員要查明并搞定的,上面那些關(guān)于切換,拷貝,cache miss,中斷之類的描述,其實都是造成收包吞吐低的原因而不是問題本身。同時,優(yōu)化掉這些問題并不是目的,目的只有一個,就是提高收包吞吐,優(yōu)化掉這些問題全部是達到目的的手段。

手段和目的混淆非常常見,這也是為什么很難有手機賣過蘋果手機的原因。看看蘋果的廣告宣傳的是什么,是產(chǎn)品本身,而很多安卓機的廣告都是在拼數(shù)據(jù),大肆宣傳什么CPU,內(nèi)存等硬件采用了什么先進的技術(shù),但是那些買手機拍視頻的網(wǎng)紅懂這些嗎?關(guān)注這些嗎?

回到協(xié)議棧話題?,F(xiàn)在,我們優(yōu)化的目標只有一個,那就是提高收包吞吐,我們的優(yōu)化目標并不是什么避免切換,降低cache miss,避免頻繁中斷這些,這些只是達到目標的手段,如果有更好地手段,我們大可不關(guān)注這些。

同樣的,把協(xié)議棧移來移去的,并不是目標,沒有什么公司會把“將協(xié)議棧移植到用戶態(tài)”,“在內(nèi)核態(tài)實現(xiàn)HTTP協(xié)議”這種作為KPI,這些都是手段而已,把協(xié)議棧在內(nèi)核態(tài)用戶態(tài)移來移去除了能展示自己高超的水平之外,對整體目標是沒有幫助的。那么很顯然,如果不用移植,能就地解決問題,那豈不更好?如果我有點石成金的本事,我干嘛還要通過辛勤地工作來讓我的老婆和女兒崇拜我?

接下來就要評估到底是用移植的手段還是就地解決的手段來解決協(xié)議棧收包吞吐低的問題。在評估之前,首先我們要明確問題的原因到底出在哪里。嗯,上面已經(jīng)列舉了一些了,切換開銷大,內(nèi)存拷貝昂貴,cache miss高,中斷太頻繁…

原因知道了,自然就能對癥下藥了,現(xiàn)在的問題是,需要評估用戶態(tài)和內(nèi)核態(tài)搞定這些問題的可行性,成本以及難度,來決定到底在什么態(tài)來解決問題,最終其實會發(fā)現(xiàn)用戶態(tài)實現(xiàn)一套協(xié)議棧是更容易的。換句話說,如果能在內(nèi)核態(tài)協(xié)議棧通過patch的方式解決這些問題,誰也不會去搞用戶態(tài)協(xié)議棧了。

不是內(nèi)核態(tài)實現(xiàn)協(xié)議棧不好,而是內(nèi)核態(tài)解決多核下的收包擴展性問題很難,因為Linux內(nèi)核設(shè)計之初并沒有考慮多核擴展性。一旦面對多核,以下的問題是積重難返的:

  • 任務切換
  • 內(nèi)存拷貝
  • CPU跳躍
  • 鎖和中斷

內(nèi)核,至少是Linux內(nèi)核沒有提供任何基礎(chǔ)設(shè)施來完美解決上述問題,隨著CPU核數(shù)越來越多,為了應對上述很多問題將會導致越來越多的trick加入內(nèi)核,內(nèi)核將會變得越來越重。

簡而言之,如果能在內(nèi)核態(tài)實現(xiàn)一個穩(wěn)定的內(nèi)核線程高效收包,那也并不是不可以,而不是說必須要搞在用戶態(tài)。然而,在內(nèi)核態(tài)實現(xiàn)這個確實不易,正是因為用戶態(tài)完成同樣的工作更加簡單,才會出現(xiàn)大量的用戶態(tài)協(xié)議棧。

中斷和輪詢

現(xiàn)在來看一下中斷和輪詢背后的哲學。

中斷本質(zhì)上是一種節(jié)約的思維影響下的產(chǎn)物,典型的“好萊塢法則”之實例。這是讓系統(tǒng)被動接收通知的方式,雖然在主觀上,這種方式可以讓系統(tǒng)“在沒有收到通知時干點別的”,但是在客觀現(xiàn)實看來,這段時間任何系統(tǒng)都沒法一心一意地去做所謂的“別的”。

都面試過吧,如果你不是什么太牛的人,那么一般很難拿到招聘方人員的聯(lián)系方式,面試完后一句“回去等通知吧”會讓多少人能平常心態(tài)等通知的同時還能繼續(xù)自己的當前工作?(我之前不能,但現(xiàn)在確實可以了,我除外)這個時候,很多人都會想,如果能問一下就好了,然而并不能。

此外,有沒有這樣的經(jīng)歷,當你專注地想做一件事時,最煩人的就是手機響。此時很多人都會手機靜音且扔遠一點,等事情做完了再去看一下。

對,這就是中斷和輪詢的特征。中斷是外界強加給你的信號,你必須被動應對,而輪詢則是你主動地在處理事情。對于中斷而言,其最大的影響就是打斷你當前工作的連續(xù)性,而輪詢則不會,完全在你自己的掌控之中。對于我自己而言,我的手機基本上是常靜音的,微信則是全部“消息免打擾”,我會選擇在自己idle的時候主動去看手機消息,以此輪詢的方式治療嚴重的電話恐懼癥。

那么如何來治療Linux內(nèi)核收包的電話恐懼癥呢?答案是暫時沒有辦法。因為如果想中斷變輪詢,那就必然得有一個內(nèi)核線程去主動poll網(wǎng)卡,而這種行為與當前的Linux內(nèi)核協(xié)議棧是不相容的,這意味著你要重新在內(nèi)核實現(xiàn)一套全新的協(xié)議棧,面臨著沒完沒了的panic,oops風險。雖然Linux已經(jīng)支持socket的busy polling模式,但這也只是緩解,而非根治!相反,完全在用戶態(tài)實現(xiàn)poll,則是一個非常干凈的方案。

關(guān)于內(nèi)存拷貝,任務切換以及cache miss,試著用上面的思路去分析,最終的結(jié)論依然是在用戶態(tài)重新實現(xiàn)一個自主的處理進程,將會比修改內(nèi)核協(xié)議棧的方式來的更加干凈。

幾乎所有的評估結(jié)論表明,用戶態(tài)協(xié)議棧是一個干凈的方案,但卻并不是唯一正確的方案,更沒有證據(jù)說用戶態(tài)協(xié)議棧是最好的方案。選擇在用戶態(tài)實現(xiàn)協(xié)議棧解決收包吞吐低的問題,完全是因為它比在內(nèi)核態(tài)解決同樣的問題更加容易,僅此而已。

意義

這里聊一個哲學問題。

有人說內(nèi)核就應該只負責控制平面,數(shù)據(jù)平面的操作全部應該交給用戶態(tài)。

好吧,這里又一個柏拉圖式的分類,實際上依然毫無意義。當然,引出類似概念的人肯定有十足的理由去說服別人相信他的分類是客觀的,有依據(jù)的,但那仍然不過是一種主觀的臆斷。繼續(xù)說下去的話,也許最終的結(jié)論將是,只有微內(nèi)核體系的操作系統(tǒng)才是最完美的操作系統(tǒng)。但事實上,我們都在用的幾乎所有操作系統(tǒng),沒有一個是完全的微內(nèi)核操作系統(tǒng),微軟的Windows不是,而Linux更是一個宏內(nèi)核系統(tǒng),沒有微內(nèi)核系統(tǒng)。

看來似乎只有一個說法能解釋這種矛盾,那就是完美的東西本來就是不存在的,我們看到的世界上所有的東西,都是柏拉圖眼中的影子,而完美則代表了抽象的本質(zhì),那是上帝的范疇,我們甚至無力去仰望!從小的時候,我們就被老師告知,世界上不存在完美的球形。

因此,如果微內(nèi)核,宏內(nèi)核根本就不存在,那么憑什么內(nèi)核態(tài)就只能處理控制平面呢?如果內(nèi)核態(tài)處理網(wǎng)絡(luò)協(xié)議棧這種做法是錯誤的,那為什么UNIX/Linux的內(nèi)核網(wǎng)絡(luò)協(xié)議棧卻存在了40多年呢?嗯,存在的即是合理的。

 

世界是進化而來的,而不是設(shè)計而來的,即便是存在設(shè)計者,也是一個蹩腳的設(shè)計者,你看,我們?nèi)梭w本身不就存在很多bug嗎?因此,即便是上帝那里,存在的也是柏拉圖眼里的影子。

 

責任編輯:武曉燕 來源: 極客重生
相關(guān)推薦

2018-10-09 15:26:19

JavaPython語言

2018-09-26 14:17:00

編程語言JavaPython

2020-08-31 19:19:27

TCPUDP視屏面試

2023-10-26 11:39:54

Linux系統(tǒng)CPU

2021-12-20 09:53:51

用戶態(tài)內(nèi)核態(tài)應用程序

2019-02-14 14:09:09

散熱器水冷一體式

2025-09-26 02:22:00

2023-11-16 12:34:00

MySQLjoin

2019-10-15 08:49:02

TCPIP協(xié)議棧

2010-09-08 15:11:36

TCP IP協(xié)議棧

2014-10-15 09:14:24

IP

2015-04-21 09:20:40

SwfitObject—C

2017-08-16 16:20:01

Linux內(nèi)核態(tài)搶占用戶態(tài)搶占

2022-12-30 07:50:05

無棧協(xié)程Linux

2023-01-06 08:04:10

GPU容器虛擬化

2023-10-09 22:18:28

Python強制縮進

2022-03-25 12:31:49

Linux根文件內(nèi)核

2024-03-28 13:13:00

Htmx前端開發(fā)框架

2025-10-31 01:22:00

2010-06-13 14:54:40

TCP IP協(xié)議棧linux
點贊
收藏

51CTO技術(shù)棧公眾號