溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

listen函數(shù)中backlog字段的作用是什么

發(fā)布時間:2021-07-28 11:32:06 來源:億速云 閱讀:178 作者:Leah 欄目:大數(shù)據(jù)

listen函數(shù)中backlog字段的作用是什么,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

通過不斷的調(diào)整client的個數(shù),我發(fā)現(xiàn)性能并沒有明顯隨著client的數(shù)量降低而降低,而是在client個數(shù)到達某個值時突然降低的。

比如我自己測的數(shù)據(jù)是在491個client時,silly的性能與redis相差無幾,但在492個client同時并發(fā)時,silly的性能銳降到redis 30%左右。

這也再次說明了應該不是malloc和memcpy造成的開銷。

在此期間,我分別嘗試加大epoll的緩沖區(qū)和增大socket預讀緩沖區(qū),都沒有明顯的效果。這也說明問題并不在IO的讀取速度上和系統(tǒng)調(diào)用開銷上。

萬般無奈之下Download下來redis3.0的源碼開始對著比,最終發(fā)現(xiàn)惟一不一樣的地方就是redis的listen的backlog竟然是511,而我的只有5.

一下子豁然開朗了,由于backlog隊列過小,導致所有的connect必須要串行執(zhí)行,大部的時間都在等待建立連接上,在將backlog的值改為511后,性能已然直逼redis。

google了一下發(fā)現(xiàn),除了redis連nginx竟然也是用511。但是記憶中backlog參數(shù)會影響未完成連請求隊列的大小,似乎增加backlog會增加syn洪水攻擊的風險。

查了好一會資料,最后發(fā)現(xiàn)man上早都指出在Linux 2.2之后listen中的backlog參數(shù)僅用于指定等待被accept的已完的socket隊列的長度。未完成連接的隊列長度則通過/proc/sys/net/ipv4/tcp_max_syn_backlog來指定。

至于為什么backlog是511而不是512, 是因為kernel中會對backlog做roundup_power_of_tow(backlog+1)處理,這里使用511實際上就是為了不浪費太多不必要的空間。

之前一直看資料上說backlog是個經(jīng)驗值,需要根據(jù)經(jīng)驗調(diào)節(jié)。然而并沒有想到,當大批量連接涌入時,backlog參數(shù)會起到這個大的影響。那么這個經(jīng)驗看來就是要估算每秒的連接建立個數(shù)了。

比如web服務器由于http的特性,需要頻繁建立斷開鏈接,因此同一時刻必然會涌入大量連接,因此可能需要更高一些的backlog值,但是對于游戲服務器來講,大多都是長連接,除了剛開服時會有大量連接涌入,大部分情況下連接的建立并不如web服務器那么頻繁。當然即使如此依然需要對每秒有多少鏈接同時進入進行估算,來衡量backlog的大小。

雖然可以不用估算直接使用backlog的最大值,但卻可能會造成‘已完成未被Accept的socket的隊列’過長,當accept出隊列后面的連接時,其已經(jīng)被遠端關閉了。

經(jīng)過測試,即使backlog為63,在局域網(wǎng)內(nèi)同時并發(fā)2000客戶端并無性能影響。


12月23日糾下補充:


1. listen的backlog值其實是會精確指定accept的隊列的,只不過它除了控制accept隊列的大小,實際上還會影響未完成的connect的隊列的大小,因此
roundup_power_of_tow(backlog+1)增大的實際是未完成connect隊列的大小。


2. /proc/sys/net/ipv4/tcp_max_syn_backlog 雖然字段名中有一個sync但其實限制的是accept隊列的大小,而并非是未完成connect隊列的大小

雖不欲寫成kernel net源碼解析的文件(實際上是怕誤人子弟:D), 但還是走一下流程證明一下吧(只針對tcp和ipv4基于3.19)。

先看listen的整個流程:

listen系統(tǒng)調(diào)用 其實是通過sock->ops->listen(sock, backlog)來完成的。

那么sock->ops->listen函數(shù)是咋來的呢,再來看socket系統(tǒng)調(diào)用, 其實是通過socket_create間接調(diào)用__sock_create來完成的。

sock->ops->listen函數(shù)則是通過__socket_create函數(shù)中調(diào)用pf->create來完成的。而pf其實是通過inet_init函數(shù)調(diào)用socket_register注冊進去的,至于什么時間調(diào)用了inet_init這里就不贅述了,畢竟這不是一篇kernel分析的文章:D.

由此我們找到pf->create實際上調(diào)用的就是inet_create函數(shù).

啊哈!接著我們終于通過inetsw_array找到sock->ops->listen函數(shù)最終其實就是inet_listen函數(shù)??梢钥吹轿覀兺ㄟ^listen傳入的backlog在經(jīng)過限大最大值之后,直接被賦給了sk_max_ack_backlog字段。

OK,再來看一下kernel收到一個sync包之后是怎么做的。

好吧,先去看icsk->icsk_af_ops->conn_request這個函數(shù)是怎么來的。

回過頭來看inetsw_array發(fā)現(xiàn)其中SOCK_STREAM中類型的prot字段其實是指向tcp_prot結(jié)構(gòu)體的。

在前面看過的的inet_create函數(shù)中的最后部分會調(diào)用sk->sk_prot->init函數(shù)。而sk_prot字段其實是通過調(diào)用sk_alloc時將inetsw_array中的prof字段賦值過去的。

因此在inet_create函數(shù)的最后sk->sk_prot->init調(diào)用的實際上是tcp_v4_init_sock函數(shù)。而在tcp_v4_init_sock函數(shù)中會將icsk->icsk_af_ops的值賦值為ipv4_specific的地址。

由此終于找到了icsk->icsk_af_ops->conn_request其實就是tcp_v4_conn_request函數(shù),此函數(shù)隨即調(diào)用tcp_conn_request函數(shù)來完成之后的內(nèi)容。

在tcp_conn_request中是通過sk_acceptq_is_full來判斷的。


從sk_acceptq_is_full函數(shù)中看到他是通過sk_max_ack_backlog字段判斷的,而這個字段在我們分析listen系統(tǒng)調(diào)用時已然看到其實就是listen傳入的那個值。

另外需要額外說明的時,在reqsk_queue_alloc中為的listen_sock::syn_table分配的空間僅僅是一個hash表,并不是際的request_sock空間。

關于listen函數(shù)中backlog字段的作用是什么問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業(yè)資訊頻道了解更多相關知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI