原創轉載請注明出處:https://www.cnblogs.com/agilestyle/p/11394930.html
OSI模型
OSI 模型把網絡互聯的框架分為應用層、表示層、會話層、傳輸層、網絡層、數據鏈路層以及物理層等七層,每個層負責不同的功能。其中,
應用層,負責為應用程序提供統一的接口。
表示層,負責把數據轉換成兼容接收系統的格式。
會話層,負責維護計算機之間的通信連接。
傳輸層,負責為數據加上傳輸表頭,形成數據包。
網絡層,負責數據的路由和轉發。
數據鏈路層,負責MAC尋址、錯誤偵測和改錯。
物理層,負責在物理網絡中傳輸數據幀。
TCP/IP 模型
TCP/IP 模型把網絡互聯的框架分為應用層、傳輸層、網絡層、網絡接口層等四層,其中,
應用層,負責向用戶提供一組應用程序,比如 HTTP、FTP、DNS 等。
傳輸層,負責端到端的通信,比如 TCP、UDP 等。
網絡層,負責網絡包的封裝、尋址和路由,比如 IP、ICMP 等。
網絡接口層,負責網絡包在物理網絡中的傳輸,比如 MAC 尋址、錯誤偵測以及通過網卡傳輸網絡幀等。
OSI 模型 與 TCP/IP 模型 的關系

Linux網絡棧
有了 TCP/IP 模型后,在進行網絡傳輸時,數據包就會按照協議棧,對上一層發來的數據進行逐層處理;然后封裝上該層的協議頭,再發送給下一層。
當然,網絡包在每一層的處理邏輯,都取決於各層采用的網絡協議。比如在應用層,一個提供 REST API 的應用,可以使用 HTTP 協議,把它需要傳輸的 JSON 數據封裝到 HTTP 協議中,然后向下傳遞給 TCP 層。
而封裝做的事情就很簡單了,只是在原來的負載前后,增加固定格式的元數據,原始的負載數據並不會被修改。比如,以通過 TCP 協議通信的網絡包為例,通過下面這張圖,可以看到,應用程序數據在每個層的封裝格式。

其中:
- 傳輸層在應用程序數據前面增加了 TCP 頭;
- 網絡層在 TCP 數據包前增加了 IP 頭;
- 而網絡接口層,又在 IP 數據包前后分別增加了幀頭和幀尾。
這些新增的頭部和尾部,都按照特定的協議格式填充,這些新增的頭部和尾部,增加了網絡包的大小,但物理鏈路中並不能傳輸任意大小的數據包。網絡接口配置的最大傳輸單元(MTU),就規定了最大的 IP 包大小。在最常用的以太網中,MTU 默認值是 1500(這也是 Linux 的默認值)。
一旦網絡包超過 MTU 的大小,就會在網絡層分片,以保證分片后的 IP 包不大於MTU 值。顯然,MTU 越大,需要的分包也就越少,網絡吞吐能力就越好。
理解了 TCP/IP 網絡模型和網絡包的封裝原理后,很容易能想到,Linux 內核中的網絡棧,其實也類似於 TCP/IP 的四層結構。如下圖所示,就是 Linux 通用 IP 網絡棧的示意圖:

從上到下來看這個網絡棧,可以發現,
- 最上層的應用程序,需要通過系統調用,來跟套接字接口進行交互;
- 套接字的下面,就是前面提到的傳輸層、網絡層和網絡接口層;
- 最底層,則是網卡驅動程序以及物理網卡設備。
網卡是發送和接收網絡包的基本設備。在系統啟動過程中,網卡通過內核中的網卡驅動程序注冊到系統中。而在網絡收發過程中,內核通過中斷跟網卡進行交互。
再結合前面提到的 Linux 網絡棧,可以看出,網絡包的處理非常復雜。所以,網卡硬中斷只處理最核心的網卡數據讀取或發送,而協議棧中的大部分邏輯,都會放到軟中斷中處理。
Linux網絡收發流程
了解了 Linux 網絡棧后,再來看看, Linux 到底是怎么收發網絡包的。
注意,以下內容都以物理網卡為例。事實上,Linux 還支持眾多的虛擬網絡設備,而它們的網絡收發流程會有一些差別。
網絡包的接收流程
先來看網絡包的接收流程。
當一個網絡幀到達網卡后,網卡會通過 DMA 方式,把這個網絡包放到收包隊列中;然后通過硬中斷,告訴中斷處理程序已經收到了網絡包。
接着,網卡中斷處理程序會為網絡幀分配內核數據結構(sk_buff),並將其拷貝到 sk_buff 緩沖區中;然后再通過軟中斷,通知內核收到了新的網絡幀。
接下來,內核協議棧從緩沖區中取出網絡幀,並通過網絡協議棧,從下到上逐層處理這個網絡幀。比如,
- 在鏈路層檢查報文的合法性,找出上層協議的類型(比如 IPv4 還是 IPv6),再去掉幀頭、幀尾,然后交給網絡層。
- 網絡層取出 IP 頭,判斷網絡包下一步的走向,比如是交給上層處理還是轉發。當網絡層確認這個包是要發送到本機后,就會取出上層協議的類型(比如 TCP 還是 UDP),去掉 IP 頭,再交給傳輸層處理。
- 傳輸層取出 TCP 頭或者 UDP 頭后,根據 <源 IP、源端口、目的 IP、目的端口> 四元組作為標識,找出對應的 Socket,並把數據拷貝到Socket 的接收緩存中。
最后,應用程序就可以使用 Socket 接口,讀取到新接收到的數據了。
為了更清晰表示這個流程,參考下圖,這張圖的左半部分表示接收流程,而圖中的粉色箭頭則表示網絡包的處理路徑。

網絡包的發送流程
了解網絡包的接收流程后,就很容易理解網絡包的發送流程。網絡包的發送流程就是上圖的右半部分,很容易發現,網絡包的發送方向,正好跟接收方向相反。
- 首先,應用程序調用 Socket API(比如 sendmsg)發送網絡包。
- 由於這是一個系統調用,所以會陷入到內核態的套接字層中。套接字層會把數據包放到 Socket 發送緩沖區中。
- 接下來,網絡協議棧從 Socket 發送緩沖區中,取出數據包;再按照 TCP/IP 棧,從上到下逐層處理。比如,傳輸層和網絡層,分別為其增加TCP 頭和 IP 頭,執行路由查找確認下一跳的 IP,並按照 MTU 大小進行分片。
- 分片后的網絡包,再送到網絡接口層,進行物理地址尋址,以找到下一跳的 MAC 地址。然后添加幀頭和幀尾,放到發包隊列中。這一切完成后,會有軟中斷通知驅動程序:發包隊列中有新的網絡幀需要發送。
- 最后,驅動程序通過 DMA ,從發包隊列中讀出網絡幀,並通過物理網卡把它發送出去。
Reference
https://time.geekbang.org/column/article/80898 (強烈推薦讀者購買此專欄,都是干貨價值一個億)
