當前位置:首頁 > 嵌入式 > 嵌入式軟件
[導讀] 作者一直認為,從應用程序到框架再到系統,使用每一個代碼是一回事理解。使用“今天,作者將研究服務器端套接字的功能。準確地説,它是bind(基於linux3.10)。

作者一直認為,從應用程序到框架再到系統,使用每一個代碼是一回事理解。使用“今天,作者將研究服務器端套接字的功能。準確地説,它是bind(基於linux3.10)。

一個最簡單的Server端例子

眾所周知,一個Server端Socket的建立,需要socket、bind、listen、accept四個步驟。 

代碼如下:

首先我們通過socket系統調用創建了一個socket,其中指定了SOCK_STREAM,而且最後一個參數為0,也就是建立了一個通常所有的TCP Socket。在這裏,我們直接給出TCP Socket所對應的ops也就是操作函數。 

如果你想知道上圖中的結構是怎麼來的,可以看下筆者以前的博客:

 

bind系統調用

bind將一個本地協議地址(protocol:ip:port)賦予一個套接字。例如32位的ipv4地址或128位的ipv6地址+16位的TCP活UDP端口號。

好了,我們直接進入Linux源碼調用棧吧。

inet_bind

inet_bind這個函數主要做了兩個操作,一是檢測是否允許bind,而是獲取可用的端口號。這邊值得注意的是。如果我們設置需要bind的端口號為0,那麼Kernel會幫我們隨機選擇一個可用的端口號來進行bind!

讓我們看下inet_bind的流程 

值得注意的是,由於對於<1024的端口號需要CAP_NET_BIND_SERVICE,我們在監聽80端口號(例如啓動nginx時候),需要使用root用户或者賦予這個可執行文件CAP_NET_BIND_SERVICE權限。

我們的bind允許綁定到0.0.0.0即INADDR_ANY這個地址上(一般都用這個),它意味着內核去選擇IP地址。對我們最直接的影響如下圖所示: 

然後,我們看下一個比較複雜的函數,即可用端口號的選擇過程inet_csk_get_port (sk->sk_prot->get_port)

inet_csk_get_port

第一段,如果bind port為0,隨機搜索可用端口號

直接上源碼,第一段代碼為端口號為0的搜索過程

由於,我們在使用bind的時候很少隨機端口號(在TCP服務器來説尤其如此),這段代碼筆者就註釋一下。一般只有一些特殊的遠程過程調用(RPC)中會使用隨機Server端隨機端口號。

第二段,找到端口號或已經指定

判斷端口號是否衝突

在上述源碼中,判斷端口號時否衝突的代碼為

上面代碼的邏輯如下圖所示: 

SO_REUSEADDR和SO_REUSEPORT

上面的代碼有點繞,筆者就講一下,對於我們日常開發要關心什麼。 我們在上面的bind裏面經常見到sk_reuse和sk_reuseport這兩個socket的Flag。這兩個Flag能夠決定是否能夠bind(綁定)成功。這兩個Flag的設置在C語言裏面如下代碼所示:

在原生JAVA中

在Netty(Netty版本 >= 4.0.16且Linux內核版本>=3.9以上)中,可以使用SO_REUSEPORT。

SO_REUSEADDR

在之前的源碼裏面,我們看到判斷bind是否衝突的時候,有這麼一個分支

如果sk2(即已bind的socket)是TCP_LISTEN狀態或者,sk2和新sk兩者都沒有設置_REUSEADDR的時候,可以判斷為衝突。

我們可以得出,如果原sock和新sock都設置了SO_REUSEADDR的時候,只要原sock不是Listen狀態,都可以綁定成功,甚至ESTABLISHED狀態也可以! 

這個在我們平常工作中,最常見的就是原sock處於TIME_WAIT狀態,這通常在我們關閉Server的時候出現,如果不設置SO_REUSEADDR,則會綁定失敗,進而啓動不來服務。而設置了SO_REUSEADDR,由於不是TCP_LISTEN,所以可以成功。 

這個特性在緊急重啓以及線下調試的非常有用,建議開啓。

SO_REUSEPORT

SO_REUSEPORT是Linux在3.9版本引入的新功能。

我們看下一般的Reactor線程模型, 

明顯的其單線程listen/accept會存在瓶頸(如果採用多線程epoll accept,則會驚羣,加WQ_FLAG_EXCLUSIVE可以解決一部分),尤其是在採用短鏈接的情況下。 鑑於此,Linux增加了SO_REUSEPORT,而之前bind中判斷是否衝突的下面代碼也是為這個參數而添加的邏輯:

這段代碼讓我們在多次bind的時候,如果設置了SO_REUSEPORT的時候不會報錯,也就是讓我們有個多線程(進程)bind/listen的能力。如下圖所示: 

而開啓了SO_REUSEPORT後,代碼棧如下:

直接在內核層面做負載均衡,將accept的任務分散到不同的線程的不同socket上(Sharding),毫無疑問可以多核能力,大幅提升連接成功後的socket分發能力。

Nginx已經採用SO_REUSEPORT

Nginx在1.9.1版本的時候引入了SO_REUSEPORT,配置如下:

 

總結

Linux有一個非常複雜的內核源代碼,希望能對讀者有所幫助。

換一批

延伸閲讀

[嵌入式軟件] Linux Kernel 5.9.1 及更早版本發現數據泄露

Linux Kernel 5.9.1 及更早版本發現數據泄露

Linux內核最近發現了一系列錯誤,這些錯誤允許攻擊者在沒有經驗證據的情況下獲得訪問機密數據和現有帳户的權限。 以下是漏洞詳情: 漏洞詳情 1.CVE-2020-26088 嚴重程度:高 net / nfc...

關鍵字: Linux kernel 漏洞

[嵌入式軟件] Linux內核社區接受了SM2國密算法被

Linux內核社區接受了SM2國密算法被

制定了大量的密碼管理方案和方案,並制定了大量的密碼管理方案和規範。 隨着近年來外部的國際貿易衝突和技術封鎖,內部互聯網的快速發展,IoT 領域的崛起,以及金融領域的變革愈演愈烈。擺脱對國外技術和產品的過度依賴,建設行業網絡安全...

關鍵字: 算法 sm2 Linux

[嵌入式大雜燴] 從串口驅動到Linux驅動模型

點擊上方「嵌入式大雜燴」,「星標公眾號」第一時間查看嵌入式筆記! 原文://www.jianshu.com/p/3a9013b9569c 作者:Linker...

關鍵字: 嵌入式 編程 Linux

[嵌入式軟件] Linux 5.8 內核,全局主題切換,全新組件上線

Linux 5.8 內核,全局主題切換,全新組件上線

10月23日,Yuqilin團隊宣佈ubuntukylin開源版本20.10正式發佈。 20.10 是優麒麟發佈的第 16 個版本,提供 9 個月的技術支持,與 Ubuntu 20.10、Lubuntu 20.10、Xubun...

關鍵字: 主題 優麒麟 Linux

[嵌入式分享] V4L2的管道驅動程序是如何被Xilinx Linux 理解的

V4L2的管道驅動程序是如何被Xilinx Linux 理解的

Xilinx提供了完整的V4L2的驅動程序,Xilinx V4L2 driver。處於最頂層的驅動程序是V4L2框架的視頻管道(Video pipeline)驅動程序,也叫橋驅動程序(bridge driver),主要代碼在文件xil...

關鍵字: Xilinx v4l2 管道驅動程序 Linux

嵌入式軟件

15674 篇文章

關注

發佈文章

技術子站

關閉