問題 1:網絡收發過程中緩沖區的位置
在 關于 Linux 網絡,你必須要知道這些 中,我曾介紹過 Linux 網絡的收發流程。這個流程涉及到了多個隊列和緩沖區,包括:
- 網卡收發網絡包時,通過 DMA 方式交互的環形緩沖區;
- 網卡中斷處理程序為網絡幀分配的,內核數據結構 sk_buff 緩沖區;
- 應用程序通過套接字接口,與網絡協議棧交互時的套接字緩沖區。
不過相應的,就會有兩個問題。
首先,這些緩沖區的位置在哪兒?是在網卡硬件中,還是在內存中?這個問題其實仔細想一下,就很容易明白——這些緩沖區都處于內核管理的內存中。
其中,環形緩沖區,由于需要 DMA 與網卡交互,理應屬于網卡設備驅動的范圍。
sk_buff 緩沖區,是一個維護網絡幀結構的雙向鏈表,鏈表中的每一個元素都是一個網絡幀(Packet)。雖然 TCP/IP 協議棧分了好幾層,但上下不同層之間的傳遞,實際上只需要操作這個數據結構中的指針,而無需進行數據復制。
套接字緩沖區,則允許應用程序,給每個套接字配置不同大小的接收或發送緩沖區。應用程序發送數據,實際上就是將數據寫入緩沖區;而接收數據,其實就是從緩沖區中讀取。至于緩沖區中數據的進一步處理,則由傳輸層的 TCP 或 UDP 協議來完成。
其次,這些緩沖區,跟前面內存部分講到的 Buffer 和 Cache 有什么關聯嗎?
這個問題其實也不難回答。我在內存模塊曾提到過,內存中提到的 Buffer ,都跟塊設備直接相關;而其他的都是 Cache。
實際上,sk_buff、套接字緩沖、連接跟蹤等,都通過 slab 分配器來管理。你可以直接通過 /proc/slabinfo,來查看它們占用的內存大小。
問題 2:內核協議棧,是通過一個內核線程的方式來運行的嗎
第二個問題,內核協議棧的運行,是按照一個內核線程的方式嗎?在內核中,又是如何執行網絡協議棧的呢?
說到網絡收發,在中斷處理文章中我曾講過,其中的軟中斷處理,就有專門的內核線程 ksoftirqd。每個 CPU 都會綁定一個 ksoftirqd 內核線程,比如, 2 個 CPU 時,就會有 ksoftirqd/0 和 ksoftirqd/1 這兩個內核線程。
不過要注意,并非所有網絡功能,都在軟中斷內核線程中處理。內核中還有很多其他機制(比如硬中斷、kworker、slab 等),這些機制一起協同工作,才能保證整個網絡協議棧的正常運行。
問題 3:最大連接數是不是受限于 65535 個端口
我們知道,無論 TCP 還是 UDP,端口號都只占 16 位,也就說其最大值也只有 65535。那是不是說,如果使用 TCP 協議,在單臺機器、單個 IP 地址時,并發連接數最大也只有 65535 呢?
對于這個問題,首先你要知道,Linux 協議棧,通過五元組來標志一個連接(即協議,源 IP、源端口、目的 IP、目的端口)。
明白了這一點,這個問題其實就有了思路。我們應該分客戶端和服務器端,這兩種場景來分析。
對客戶端來說,每次發起 TCP 連接請求時,都需要分配一個空閑的本地端口,去連接遠端的服務器。由于這個本地端口是獨占的,所以客戶端最多只能發起 65535 個連接。
對服務器端來說,其通常監聽在固定端口上(比如 80 端口),等待客戶端的連接。根據五元組結構,我們知道,客戶端的 IP 和端口都是可變的。如果不考慮 IP 地址分類以及資源限制,服務器端的理論最大連接數,可以達到 2 的 48 次方(IP 為 32 位,端口號為 16 位),遠大于 65535。
所以,綜合來看,客戶端最大支持 65535 個連接,而服務器端可支持的連接數是海量的。當然,由于 Linux 協議棧本身的性能,以及各種物理和軟件的資源限制等,這么大的連接數,還是遠遠達不到的(實際上,C10M 就已經很難了)。