【網(wǎng)絡(luò)】Linux 跨機網(wǎng)路通信和本機網(wǎng)路通信

一、關(guān)于 skb

一個報文的產(chǎn)生和發(fā)送,都需要硬件和軟件的完美配合。
硬件層面接收到報文之后,做一系列的初始化操作,之后驅(qū)動才開始把一個封包封裝為skb。
當(dāng)然這是在x86架構(gòu)下,如果是在cavium架構(gòu)下,封包是wqe形式存在。
不管是skb還是wqe,都僅僅是一種手段,一種達到完成報文傳輸所采用的一種解決方案,一種方法而已。
或許處理方案的具體實現(xiàn)細節(jié)差別萬千,但是基本的原理,都是殊途同歸,萬變不離其宗。
skb的產(chǎn)生,讓Linux協(xié)議棧旅程的開啟,具備了最基本的條件,接下來的協(xié)議棧之旅,才會更加精彩。

image.png

寫作本文的原因是現(xiàn)在本機網(wǎng)絡(luò) IO 應(yīng)用非常廣。
在 php 中 一般 nginx 和 php-fpm 是通過 127.0.0.1 來進行通信的;
在微服務(wù)中,由于 side car 模式的應(yīng)用,本機網(wǎng)絡(luò)請求更是越來越多。
所以,如果能深度理解這個問題在各種網(wǎng)絡(luò)通信應(yīng)用的技術(shù)實踐中將非常的有意義。

今天咱們就把 127.0.0.1 本機網(wǎng)絡(luò)通信相關(guān)問題搞搞清楚!

為了方便討論,我把這個問題拆分成3問:

1)127.0.0.1 本機網(wǎng)絡(luò) IO 需要經(jīng)過網(wǎng)卡嗎?
2)和外網(wǎng)網(wǎng)絡(luò)通信相比,在內(nèi)核收發(fā)流程上有啥差別?
3)使用 127.0.0.1 能比 192.168.x.x 更快嗎?

二、跨機網(wǎng)路通信

跨機數(shù)據(jù)發(fā)送

image.png

在上面這幅圖中,我們看到用戶數(shù)據(jù)被拷貝到內(nèi)核態(tài),然后經(jīng)過協(xié)議棧處理后進入到了 RingBuffer 中。隨后網(wǎng)卡驅(qū)動真正將數(shù)據(jù)發(fā)送了出去。當(dāng)發(fā)送完成的時候,是通過硬中斷來通知 CPU,然后清理 RingBuffer。

跨機數(shù)據(jù)接收

image.png

當(dāng)數(shù)據(jù)包到達另外一臺機器的時候,Linux 數(shù)據(jù)包的接收過程開始了。
當(dāng)網(wǎng)卡收到數(shù)據(jù)以后,CPU發(fā)起一個中斷,以通知 CPU 有數(shù)據(jù)到達。
當(dāng)CPU收到中斷請求后,會去調(diào)用網(wǎng)絡(luò)驅(qū)動注冊的中斷處理函數(shù),觸發(fā)軟中斷。
ksoftirqd 檢測到有軟中斷請求到達,開始輪詢收包,收到后交由各級協(xié)議棧處理。
當(dāng)協(xié)議棧處理完并把數(shù)據(jù)放到接收隊列的之后,喚醒用戶進程(假設(shè)是阻塞方式)。

跨機網(wǎng)絡(luò)通信匯總

關(guān)于跨機網(wǎng)絡(luò)通信的理解,可以通俗地用下面這張圖來總結(jié)一下:


image.png
image.png

三、本機網(wǎng)路通信

本機網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送過程

前面,我們看到了跨機時整個網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送過程 。

在本機網(wǎng)絡(luò) IO 的過程中,流程會有一些差別。
為了突出重點,本節(jié)將不再介紹整體流程,而是只介紹和跨機邏輯不同的地方。
有差異的地方總共有兩個,分別是路由和驅(qū)動程序。

對于本機網(wǎng)絡(luò) IO 來說,特殊之處在于在 local 路由表中就能找到路由項,對應(yīng)的設(shè)備都將使用 loopback 網(wǎng)卡,也就是我們常見的 lO。

# ip route list table local

broadcast 10.0.8.0 dev eth0 proto kernel scope link src 10.0.8.7
local 10.0.8.7 dev eth0 proto kernel scope host src 10.0.8.7
broadcast 10.0.11.255 dev eth0 proto kernel scope link src 10.0.8.7
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 172.17.0.0 dev docker0 proto kernel scope link src 172.17.0.1
local 172.17.0.1 dev docker0 proto kernel scope host src 172.17.0.1
broadcast 172.17.255.255 dev docker0 proto kernel scope link src 172.17.0.1

從上述結(jié)果可以看出,對于目的是 127.0.0.1 的路由在 local 路由表中就能夠找到了。

對于是本機的網(wǎng)絡(luò)請求,設(shè)備將全部都使用 lo 虛擬網(wǎng)卡,接下來的網(wǎng)絡(luò)層仍然和跨機網(wǎng)絡(luò) IO 一樣。

本機網(wǎng)絡(luò) IO 需要進行 IP 分片嗎?

因為和正常的網(wǎng)絡(luò)層處理過程一樣,如果 skb 大于 MTU 的話,仍然會進行分片。
只不過 lo 的 MTU 比 Ethernet 要大很多。
通過 ifconfig 命令就可以查到,普通網(wǎng)卡一般為 1500,而 lO 虛擬接口能有 65535。

# ifconfig

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        ether 02:42:a1:38:e8:72  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.8.7  netmask 255.255.252.0  broadcast 10.0.11.255
        inet6 fe80::5054:ff:fe84:973  prefixlen 64  scopeid 0x20<link>
        ether 52:54:00:84:09:73  txqueuelen 1000  (Ethernet)
        RX packets 96148964  bytes 11053177121 (10.2 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 96846934  bytes 16037210490 (14.9 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2886  bytes 298614 (291.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2886  bytes 298614 (291.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

為什么我把“驅(qū)動”加個引號呢,因為 loopback 是一個純軟件性質(zhì)的虛擬接口,并沒有真正意義上的驅(qū)動。
在鄰居子系統(tǒng)函數(shù)中經(jīng)過處理,進入到網(wǎng)絡(luò)設(shè)備子系統(tǒng),只有觸發(fā)完軟中斷,發(fā)送過程就算是完成了。

本機網(wǎng)絡(luò)數(shù)據(jù)的接收過程

在跨機的網(wǎng)絡(luò)包的接收過程中,需要經(jīng)過硬中斷,然后才能觸發(fā)軟中斷。

而在本機的網(wǎng)絡(luò) IO 過程中,由于并不真的過網(wǎng)卡,所以網(wǎng)卡實際傳輸,硬中斷就都省去了。直接從軟中斷開始,送進協(xié)議棧。

網(wǎng)絡(luò)再往后依次是傳輸層,最后喚醒用戶進程,這里就不多展開了。

我們來總結(jié)一下本機網(wǎng)絡(luò)通信的內(nèi)核執(zhí)行流程:

image.png

回想下跨機網(wǎng)絡(luò) IO 的流程:


image.png

四、開篇三個問題的答案

1)問題1:127.0.0.1 本機網(wǎng)絡(luò) IO 需要經(jīng)過網(wǎng)卡嗎?

通過本文的敘述,我們確定地得出結(jié)論,不需要經(jīng)過網(wǎng)卡。即使了把網(wǎng)卡拔了本機網(wǎng)絡(luò)是否還可以正常使用的。

2)問題2:數(shù)據(jù)包在內(nèi)核中是個什么走向,和外網(wǎng)發(fā)送相比流程上有啥差別?

總的來說,本機網(wǎng)絡(luò) IO 和跨機 IO 比較起來,確實是節(jié)約了一些開銷。發(fā)送數(shù)據(jù)不需要進 RingBuffer 的驅(qū)動隊列,直接把 skb 傳給接收協(xié)議棧(經(jīng)過軟中斷)。

但是在內(nèi)核其它組件上可是一點都沒少:系統(tǒng)調(diào)用、協(xié)議棧(傳輸層、網(wǎng)絡(luò)層等)、網(wǎng)絡(luò)設(shè)備子系統(tǒng)、鄰居子系統(tǒng)整個走了一個遍。連“驅(qū)動”程序都走了(雖然對于回環(huán)設(shè)備來說只是一個純軟件的虛擬出來的東東)。所以即使是本機網(wǎng)絡(luò) IO,也別誤以為沒啥開銷。

3)問題3:使用 127.0.0.1 能比 192.168.x 更快嗎?

先說結(jié)論:我認為這兩種使用方法在性能上沒有啥差別。

我覺得有相當(dāng)大一部分人都會認為訪問本機server 的話,用 127.0.0.1 更快。原因是直覺上認為訪問 IP 就會經(jīng)過網(wǎng)卡。

其實內(nèi)核知道本機上所有的 IP,只要發(fā)現(xiàn)目的地址是本機 IP 就可以全走 loopback 回環(huán)設(shè)備了。
本機其它 IP 和 127.0.0.1 一樣,也是不用過物理網(wǎng)卡的,所以訪問它們性能開銷基本一樣!

五、 參考

How SKBs work - Linux kernel
http://vger.kernel.org/~davem/skb.html

一篇解讀Linux網(wǎng)絡(luò)協(xié)議棧
https://zhuanlan.zhihu.com/p/475319464

你真的了解127.0.0.1和0.0.0.0的區(qū)別?
http://www.52im.net/thread-2928-1-1.html

深入操作系統(tǒng),徹底搞懂127.0.0.1本機網(wǎng)絡(luò)通信
http://www.52im.net/thread-3590-1-1.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容