您好,登錄后才能下訂單哦!
listen函數中backlog字段的作用是什么,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
通過不斷的調整client的個數,我發現性能并沒有明顯隨著client的數量降低而降低,而是在client個數到達某個值時突然降低的。
比如我自己測的數據是在491個client時,silly的性能與redis相差無幾,但在492個client同時并發時,silly的性能銳降到redis 30%左右。
這也再次說明了應該不是malloc和memcpy造成的開銷。
在此期間,我分別嘗試加大epoll的緩沖區和增大socket預讀緩沖區,都沒有明顯的效果。這也說明問題并不在IO的讀取速度上和系統調用開銷上。
萬般無奈之下Download下來redis3.0的源碼開始對著比,最終發現惟一不一樣的地方就是redis的listen的backlog竟然是511,而我的只有5.
一下子豁然開朗了,由于backlog隊列過小,導致所有的connect必須要串行執行,大部的時間都在等待建立連接上,在將backlog的值改為511后,性能已然直逼redis。
google了一下發現,除了redis連nginx竟然也是用511。但是記憶中backlog參數會影響未完成連請求隊列的大小,似乎增加backlog會增加syn洪水攻擊的風險。
查了好一會資料,最后發現man上早都指出在Linux 2.2之后listen中的backlog參數僅用于指定等待被accept的已完的socket隊列的長度。未完成連接的隊列長度則通過/proc/sys/net/ipv4/tcp_max_syn_backlog來指定。
至于為什么backlog是511而不是512, 是因為kernel中會對backlog做roundup_power_of_tow(backlog+1)處理,這里使用511實際上就是為了不浪費太多不必要的空間。
之前一直看資料上說backlog是個經驗值,需要根據經驗調節。然而并沒有想到,當大批量連接涌入時,backlog參數會起到這個大的影響。那么這個經驗看來就是要估算每秒的連接建立個數了。
比如web服務器由于http的特性,需要頻繁建立斷開鏈接,因此同一時刻必然會涌入大量連接,因此可能需要更高一些的backlog值,但是對于游戲服務器來講,大多都是長連接,除了剛開服時會有大量連接涌入,大部分情況下連接的建立并不如web服務器那么頻繁。當然即使如此依然需要對每秒有多少鏈接同時進入進行估算,來衡量backlog的大小。
雖然可以不用估算直接使用backlog的最大值,但卻可能會造成‘已完成未被Accept的socket的隊列’過長,當accept出隊列后面的連接時,其已經被遠端關閉了。
經過測試,即使backlog為63,在局域網內同時并發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系統調用 其實是通過sock->ops->listen(sock, backlog)來完成的。
那么sock->ops->listen函數是咋來的呢,再來看socket系統調用, 其實是通過socket_create間接調用__sock_create來完成的。
sock->ops->listen函數則是通過__socket_create函數中調用pf->create來完成的。而pf其實是通過inet_init函數調用socket_register注冊進去的,至于什么時間調用了inet_init這里就不贅述了,畢竟這不是一篇kernel分析的文章:D.
由此我們找到pf->create實際上調用的就是inet_create函數.
啊哈!接著我們終于通過inetsw_array找到sock->ops->listen函數最終其實就是inet_listen函數。可以看到我們通過listen傳入的backlog在經過限大最大值之后,直接被賦給了sk_max_ack_backlog字段。
OK,再來看一下kernel收到一個sync包之后是怎么做的。
好吧,先去看icsk->icsk_af_ops->conn_request這個函數是怎么來的。
回過頭來看inetsw_array發現其中SOCK_STREAM中類型的prot字段其實是指向tcp_prot結構體的。
在前面看過的的inet_create函數中的最后部分會調用sk->sk_prot->init函數。而sk_prot字段其實是通過調用sk_alloc時將inetsw_array中的prof字段賦值過去的。
因此在inet_create函數的最后sk->sk_prot->init調用的實際上是tcp_v4_init_sock函數。而在tcp_v4_init_sock函數中會將icsk->icsk_af_ops的值賦值為ipv4_specific的地址。
由此終于找到了icsk->icsk_af_ops->conn_request其實就是tcp_v4_conn_request函數,此函數隨即調用tcp_conn_request函數來完成之后的內容。
在tcp_conn_request中是通過sk_acceptq_is_full來判斷的。
從sk_acceptq_is_full函數中看到他是通過sk_max_ack_backlog字段判斷的,而這個字段在我們分析listen系統調用時已然看到其實就是listen傳入的那個值。
另外需要額外說明的時,在reqsk_queue_alloc中為的listen_sock::syn_table分配的空間僅僅是一個hash表,并不是際的request_sock空間。
關于listen函數中backlog字段的作用是什么問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。