2 萬字綜述:Linux 網絡性能終極指南
原文:Linux Network Performance Ultimate Guide[1]
作者:Kien Nguyen-Tuan
參考資料
-
https://github.com/leandromoreira/linux-network-performance-parameters/[2]
-
https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf[3]
-
https://www.coverfire.com/articles/queuei
ng-in-the-linux-network-stack/[4]
-
https://blog.cloudflare.com/how-to-achieve-low-latency/[5]
-
https://blog.cloudflare.com/how-to-receive-a-million-packets/[6]
-
https://beej.us/guide/bgnet/html/[7]
-
https://blog.csdn.net/armlinuxww/article/details/111930788[8]
-
https://www.ibm.com/docs/en/linux-on-systems?topic=recommendations-network-performance-tuning[9]
1、Linux 網絡棧
資料來源:
-
https://blog.packagecloud.io/illusterated-guide-monitoring-tuning-linux-networking-stack-receiving-data/[10]
-
https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-receiving-data/[11]
-
https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-sending-data/[12]
-
https://www.sobyte.net/post/2022-10/linux-net-snd-rcv/[13]
-
https://juejin.cn/post/7106345054368694280[14]
-
https://openwrt.org/docs/guide-developer/networking/praxis[15]
-
https://blog.51cto.com/u_15169172/2710604[16]、
-
https://sn0rt.github.io/media/paper/TCPlinux.pdf[17]
-
https://medium.com/coccoc-engineering-blog/linux-network-ring-buffers-cea7ead0b8e8[18]
-
https://sn0rt.github.io/media/paper/TCPlinux.pdf
-
完整的網絡數據流:
-
這是入門指南。在進行任何調整之前,請確保我們瞭解運行 Linux 的計算機如何接收數據包。
-
Linux 隊列:
注意:以下部分將大量使用 sysctl
。如果您不熟悉此命令,請查看 HOWTO#sysctl 部分 [19]。
1.1 Linux 網絡數據包接收
-
詳細版本您可以查看 PackageCloud 的文章 [20]。 - 在網絡設備中,NIC 通常會發出 IRQ 來表示數據包已到達並準備好進行處理。 - IRQ(中斷請求)是發送給處理器的硬件信號,指示處理器暫停當前活動並處理某些外部事件,例如鍵盤輸入或鼠標移動。 - 在 Linux 中,IRQ 映射存儲在 /proc/interrupts 中。 - 當 Linux 內核執行 IRQ 處理程序時,它會以非常高的優先級運行,並且通常會阻止生成其他 IRQ。 因此,設備驅動程序中的 IRQ 處理程序必須儘快執行,並將所有長時間運行的工作推遲到此上下文之外執行。 這就是 softIRQ 系統存在的原因。 -softIRQ 系統是內核用來處理設備驅動程序 IRQ 上下文之外的工作的系統。 對於網絡設備,softIRQQ 系統負責處理傳入的數據包
-
初始設置(從步驟 1-4):
“
注意:有些 NIC 是 “多隊列”NIC。上圖僅顯示了單個環形緩衝區以方便理解,但根據您使用的 NIC 和硬件設置,系統中可能會有多個隊列。請查看在 CPU 之間共享數據包處理負載部分以瞭解詳細信息。
-
數據包到達網卡
-
NIC 進行驗證
MAC
(如果不是混雜模式 [21])並FCS
決定放棄或繼續 -
NIC 將 DMA(直接內存訪問)[22]
sk_buff
數據包放入 RAM - 在稱爲或skb
(套接字內核緩衝區 - SKB[23] )的內核數據結構中。 -
NIC 將數據包_的引用_放入接收環形緩衝區隊列,
rx
直到rx-usecs
超時或rx-frames
。讓我們來談談 RX 環形緩衝區:
-
它是一個循環緩衝區 [24],溢出時會簡單地覆蓋現有數據。
-
它_不包含數據包數據_
skbs
。而是由指向 DMA 到 RAM 的描述符組成(步驟 2)。 -
固定大小、FIFO 且位於 RAM(當然)。
-
NIC 引發
HardIRQ
“硬中斷”。egrep “CPU0|eth3” /proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 110: 0 0 0 0 0 0 IR-PCI-MSI-edge eth3-rx-0 111: 0 0 0 0 0 0 IR-PCI-MSI-edge eth3-rx-1 112: 0 0 0 0 0 0 IR-PCI-MSI-edge eth3-rx-2 113: 2 0 0 0 0 0 IR-PCI-MSI-edge eth3-rx-3 114: 0 0 0 0 0 0 IR-PCI-MSI-edge eth3-tx
-
HardIRQ
:來自硬件的中斷,稱爲 “上半部” 中斷。 -
當 NIC 接收到傳入數據時,它會使用 DMA 將數據複製到內核緩衝區中。NIC 通過發出 HardIRQ 通知內核此數據。這些中斷由中斷處理程序處理,這些中斷處理程序只做很少的工作,因爲它們已經中斷了另一個任務,並且無法自行中斷。
-
HardIRQ 在 CPU 使用率方面可能會很昂貴,尤其是在持有內核鎖的情況下。如果它們執行時間過長,將導致 CPU 無法響應其他 HardIRQ,因此內核引入了
SoftIRQs
(Soft Interrupts),這樣可以將 HardIRQ 處理程序中耗時的部分移到 SoftIRQ 處理程序中慢慢處理。我們在接下來的步驟中會討論 SoftIRQ。 -
可以看到 HardIRQ,其中
/proc/interrupts
每個隊列在第一列中都分配有一箇中斷向量。這些中斷向量在系統啓動或加載 NIC 設備驅動程序模塊時初始化。每個 RX 和 TX 隊列都分配有一個唯一的向量,該向量會通知中斷處理程序中斷來自哪個 NIC / 隊列。這些列以計數器值的形式表示傳入中斷的數量:
-
CPU 運行
IRQ handler
運行驅動程序代碼的。 -
驅動程序將調度一個 NAPI[25],清除 NIC 上的 HardIRQ,以便它可以爲新數據包到達生成 IRQ。
-
驅動觸發
SoftIRQ (NET_RX_SOFTIRQ)
。
```
ps aux | grep ksoftirq
# ksotirqd/<cpu-number>
root 13 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/0]
root 22 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/1]
root 28 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/2]
root 34 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/3]
root 40 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/4]
root 46 0.0 0.0 0 0 ? S Dec13 0:00 [ksoftirqd/5]
```
```
watch -n1 grep RX /proc/softirqs
watch -n1 grep TX /proc/softirqs
```
-
監控命令:
-
讓我們來談談
SoftIRQ
,也稱爲 “下半部” 中斷。它是一個內核例程,計劃在其他任務不會被中斷時運行。 -
目的:耗盡網絡適配器接收 Rx 環形緩衝區。
-
這些例程以進程的形式運行
ksoftirqd/cpu-number
,並調用驅動程序特定的代碼函數。 -
檢查命令:
- NAPI 從 rx 環形緩衝區輪詢數據。
-
如果 SoftIRQ 運行時間不夠長,傳入數據的速率可能會超過內核耗盡緩衝區的能力。結果,NIC 緩衝區將溢出,流量將丟失。有時,需要增加 SoftIRQ 在 CPU 上運行的時間。這稱爲
netdev_budget
。sysctl net.core.netdev_budget net.core.netdev_budget = 300
-
netdev_budget_usecs
:1 個 NAPI 輪詢週期的最大微秒數。當netdev_budget_usecs
輪詢週期已過或處理的數據包數達到時,輪詢將退出netdev_budget
。sysctl net.core.netdev_budget_usecs net.core.netdev_budget_usecs = 8000
-
dev_weight
:內核在 NAPI 中斷上可以處理的最大數據包數量,這是一個 PER-CPU 變量。對於支持 LRO 或 GRO_HW 的驅動程序,硬件聚合數據包在此計爲一個數據包。 -
檢查命令,默認值爲 300,這意味着 SoftIRQ 進程在離開 CPU 之前要從 NIC 中消耗 300 條消息。
-
檢查命令:
-
NAPI 的編寫是爲了提高處理傳入卡的數據包的效率。我們都知道,HardIRQ 的代價很高,因爲它們不能被中斷。即使使用_中斷合併_(稍後會詳細介紹),中斷處理程序也會完全獨佔一個 CPU 核心。NAPI 的設計允許驅動程序進入輪詢模式,而不是每次接收所需的數據包時都進入 HardIRQ 模式。
-
步驟 1->9 簡要:
-
netdev_budget_usecs
輪詢例程具有預算,它通過使用超時或netdev_budget
數據包來確定允許代碼使用的 CPU 時間dev_weight
。這是爲了防止 SoftIRQ 獨佔 CPU。完成後,內核將退出輪詢例程並重新準備,然後整個過程將重複進行。 -
讓我們討論一下
netdev_budget_usecs
超時或netdev_budget
數據dev_weight
包:sysctl net.core.dev_weight net.core.dev_weight = 64
-
Linux 還爲 分配內存
sk_buff
。 -
Linux 填充元數據:協議、接口、setmatchheader、刪除以太網
-
Linux 將 skb 傳遞給內核棧(
netif_receive_skb
) -
它設置網絡頭,克隆
skb
到 taps(即 tcpdump)並將其傳遞給 tc ingress -
netdev_max_backlog
數據包按照以下算法處理到指定大小的 qdiscdefault_qdisc
:
```
sysctl net.core.netdev_max_backlog
net.core.netdev_max_backlog = 1000
```
-
默認情況下,值爲 1000(取決於網絡接口驅動程序):
ifconfig <interface> | grep rxqueuelen
-
rxqueuelen
:接收隊列長度,是一個 TCP/IP 堆棧網絡接口值,用於設置網絡接口設備每個內核接收隊列允許的數據包數量。 -
default_qdisc
:用於網絡設備的默認排隊規則。這允許使用替代方法覆蓋 pfifo_fast 的默認值。由於默認排隊規則是在沒有附加參數的情況下創建的,因此最適合無需配置即可正常工作的排隊規則,如隨機公平隊列 (sfq)、CoDel (codel) 或公平隊列 CoDel (fq_codel)。有關每個 QDisc 的完整詳細信息,請參見man tc <qdisc-name>
(例如man tc fq_codel
)。 -
netdev_max_backlog
:Linux 內核中的一個隊列,在從 NIC 接收流量後,但在協議棧(IP、TCP 等)處理之前,流量會存儲在該隊列中。每個 CPU 核心都有一個積壓隊列。給定核心的隊列可以自動增長,包含的數據包數量最多爲設置指定的最大值netdev_max_backlog
。 -
換句話說,當接口接收數據包的速度比內核處理數據包的速度快時,這是在 INPUT 端(接收 dsic)排隊的數據包的最大數量。
-
檢查命令,默認值爲 1000。
-
它調用
ip_rcv
並處理 IP 數據包 -
它調用 netfilter(
PREROUTING
) -
它查看路由表,看看是轉發還是本地
-
如果是本地的,則調用 netfilter (
LOCAL_IN
) -
它調用 L4 協議(例如
tcp_v4_rcv
) -
它找到了正確的插座
-
它進入 tcp 有限狀態機
-
將數據包排入接收緩衝區並按
tcp_rmem
規則調整大小
-
net.core.rmem_max
和net.ipv4.tcp-rmem
'最大值'之間,較大值優先 [26]。 -
增加此緩衝區以支持擴展到更大的窗口大小。更大的窗口會增加在需要確認 (ACK) 之前要傳輸的數據量。這可以減少總體延遲並提高吞吐量。
-
此設置通常設置爲非常保守的值 262,144 字節。建議將此值設置爲內核允許的最大值。4.x 內核接受超過 16 MB 的值。
-
min:TCP 套接字使用的接收緩衝區的最小大小。即使在中等內存壓力下,每個 TCP 套接字都可以保證這一點。默認值:4 KB。
-
默認值:TCP 套接字使用的接收緩衝區的初始大小。此值覆蓋
net.core.rmem_default
其他協議使用的值。默認值:131072 字節。此值導致初始窗口爲 65535。 -
max:爲 TCP 套接字自動選擇的接收緩衝區允許的最大接收緩衝區大小。此值不會覆蓋
net.core.rmem_max
。使用 調用setsockopt()
將SO_RCVBUF
禁用該套接字的接收緩衝區大小的自動調整,在這種情況下將忽略此值。SO_RECVBUF
設置 TCP 接收緩衝區的固定大小,它將覆蓋tcp_rmem
,並且內核將不再動態調整緩衝區。設置的最大值SO_RECVBUF
不能超過net.core.rmem_max
。通常,我們不會使用它。默認值:介於 131072 和 6MB 之間,取決於 RAM 大小。 -
如果啓用了 tcp_moderate_rcvbuf,內核將自動調整接收緩衝區
-
tcp_rmem
:包含 3 個值,分別代表 TCP 套接字接收緩衝區的最小大小、默認大小和最大大小。 -
net.core.rmem_max
:TCP 接收緩衝區大小的上限。
-
內核將發出信號,表示有數據可供應用程序使用(epoll 或任何輪詢系統)
-
應用程序喚醒並讀取數據
1.2 Linux 內核網絡傳輸
雖然比接收邏輯邏輯簡單,但發送邏輯仍然值得研究。
-
應用程序發送消息(
sendmsg
或其他) -
TCP 發送報文分配 skb_buff
-
它將 skb 放入大小
tcp_wmem
爲sysctl net.ipv4.tcp_wmem net.ipv4.tcp_wmem = 4096 16384 262144
-
TCP 發送緩衝區的大小將由內核在 min 和 max 之間動態調整。初始大小爲默認大小。
-
net.core.wmem_max
:TCP 發送緩衝區大小的上限。與 類似net.core.rmem_max
(但用於傳輸)。 -
min:爲 TCP 套接字保留的發送緩衝區內存量。每個 TCP 套接字自誕生之日起就有權使用它。默認值:4K
-
默認值:TCP 套接字使用的發送緩衝區的初始大小。此值覆蓋其他協議使用的 net.core.wmem_default。它通常低於
net.core.wmem_default
。默認值:16K -
max:允許用於自動調整 TCP 套接字發送緩衝區的最大內存量。此值不會覆蓋 net.core.wmem_max。使用 調用
setsockopt()
將SO_SNDBUF
禁用該套接字發送緩衝區大小的自動調整,在這種情況下將忽略此值。SO_SNDBUF
設置發送緩衝區的固定大小,它將覆蓋tcp_wmem
,並且內核將不再動態調整緩衝區。SO_SNDBUF 設置的最大值不能超過net.core.wmem_max
。通常,我們不會使用它。默認值:介於 64K 和 4MB 之間,取決於 RAM 大小。 -
tcp_wmem
:包含 3 個值,分別代表 TCP 套接字發送緩衝區的最小大小、默認大小和最大大小。 -
檢查命令:
-
構建 TCP 標頭(源端口和目標端口、校驗和)
-
調用 L3 處理程序(在本例中
ipv4
爲tcp_write_xmit
和tcp_transmit_skb
) -
L3(
ip_queue_xmit
) 完成其工作:構建 IP 報頭並調用 netfilter(LOCAL_OUT
) -
調用輸出路由操作
-
調用 netfilter(
POST_ROUTING
) -
對數據包進行分片 (
ip_output
) -
調用 L2 發送函數 (
dev_queue_xmit
) -
txqueuelen
使用其算法將長度爲輸出(QDisc)的隊列輸入default_qdisc
-
默認情況下,值爲 1000(取決於網絡接口驅動程序):
ifconfig <interface> | grep txqueuelen
-
txqueuelen
:傳輸隊列長度,是 TCP/IP 堆棧網絡接口值,用於設置網絡接口設備每個內核傳輸隊列允許的數據包數量。 -
default_qdisc
:用於網絡設備的默認排隊規則。這允許使用替代方法覆蓋 pfifo_fast 的默認值。由於默認排隊規則是在沒有附加參數的情況下創建的,因此最適合無需配置即可正常工作的排隊規則,如隨機公平隊列 (sfq)、CoDel (codel) 或公平隊列 CoDel (fq_codel)。有關每個 QDisc 的完整詳細信息,請參見man tc <qdisc-name>
(例如man tc fq_codel
)。
-
驅動程序代碼將數據包排入
ring buffer tx
-
soft IRQ (NET_TX_SOFTIRQ)
驅動程序將在超時後執行tx-usecs
或tx-frames
-
重新啓用 NIC 的硬 IRQ
-
驅動程序會將所有要發送的數據包映射到某個 DMA 區域
-
NIC 通過 DMA 從 RAM 獲取數據包並進行傳輸
-
傳輸完成後,NIC 將發出一個
hard IRQ
信號來表示傳輸完成 -
驅動程序將處理此 IRQ(將其關閉)
-
並安排(
soft IRQ
)NAPI 投票系統 -
NAPI 將處理接收數據包信號並釋放 RAM
2、網絡性能調優
調整 NIC 以獲得最佳吞吐量和延遲是一個複雜的過程,需要考慮許多因素。沒有可以廣泛適用於每個系統的通用配置。
網絡性能調整需要考慮一些因素。請注意,您的設備中的接口卡名稱可能不同,請更改相應的值。
接下來,讓我們跟蹤數據包的接收(和傳輸)並進行一些調整。
2.1 快速指南
2.1.1/proc/net/softnet_stat
&/proc/net/sockstat
在我們繼續之前,讓我們先討論一下/proc/net/softnet_stat
&/proc/net/sockstat
因爲這些文件將會被大量使用。
cat /proc/net/softnet_stat
0000272d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000034d9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
00002c83 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000002
0000313d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000003
00003015 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000004
000362d2 00000000 000000d2 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000005
-
softnet_stat 文件的每一行代表從 CPU0 開始的一個 CPU 核心。
-
每列的統計數據以十六進制提供
-
第一列是中斷處理程序接收的幀數。
-
netdev_max_backlog
第二列是由於超出而丟失的幀數。 -
netdev_budget
第三列是當仍有工作要做時 ksoftirqd 耗盡 CPU 時間的次數。 -
其他列可能因 Linux 版本的不同而有所不同。
cat /proc/net/sockstat
sockets: used 937
TCP: inuse 21 orphan 0 tw 0 alloc 22 mem 5
UDP: inuse 9 mem 5
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
-
檢查
mem
字段。它是通過sk_buff->truesize
對所有套接字求和來計算的。 -
更多詳細信息請點擊此處 [27]
2.1.2ss
-
ss
是另一個用於調查套接字的實用程序。它用於轉儲套接字統計信息。它允許顯示類似於的信息netstat
。它可以顯示比其他工具更多的 TCP 和狀態信息。 -
更多信息請參見手冊頁:
man ss
。 -
例如,檢查套接字內存使用情況:
ss -tm
# -m, --memory
# Show socket memory usage. The output format is:
# skmem:(r<rmem_alloc>,rb<rcv_buf>,t<wmem_alloc>,tb<snd_buf>,
# f<fwd_alloc>,w<wmem_queued>,o<opt_mem>,
# bl<back_log>,d<sock_drop>)
# <rmem_alloc>
# the memory allocated for receiving packet
# <rcv_buf>
# the total memory can be allocated for receiving packet
# <wmem_alloc>
# the memory used for sending packet (which has been sent to layer 3)
# <snd_buf>
# the total memory can be allocated for sending packet
# <fwd_alloc>
# the memory allocated by the socket as cache, but not used for receiving/sending packet yet. If need memory to send/receive packet, the memory in this
# cache will be used before allocate additional memory.
# <wmem_queued>
# The memory allocated for sending packet (which has not been sent to layer 3)
# <ropt_mem>
# The memory used for storing socket option, e.g., the key for TCP MD5 signature
# <back_log>
# The memory used for the sk backlog queue. On a process context, if the process is receiving packet, and a new packet is received, it will be put into the
# sk backlog queue, so it can be received by the process immediately
# <sock_drop>
# the number of packets dropped before they are de-multiplexed into the socket
# -t, --tcp
# Display TCP sockets.
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.56.102:ssh 192.168.56.1:56328
skmem:(r0,rb369280,t0,tb87040,f0,w0,o0,bl0,d0)
# rcv_buf: 369280 bytes
# snd_buf: 87040 bytes
2.1.3netstat
-
命令行實用程序,可打印有關開放網絡連接和協議棧統計信息的信息。它從
/proc/net/
文件系統檢索有關網絡子系統的信息。這些文件包括: -
/proc/net/dev
(設備信息) -
/proc/net/tcp
(TCP 套接字信息) -
/proc/net/unix
(Unix 域套接字信息) -
有關
netstat
及其引用文件的更多信息/proc/net/
,請參閱netstat
手冊頁:man netstat
。
2.1.4sysctl
- 而不是直接通過文件系統
echo
中的值來修改系統變量/proc
:
echo "value" > /proc/sys/location/variable
- 該
sysctl
命令可用於更改系統 / 網絡設置。它提供了臨時覆蓋默認設置值的方法,用於評估目的,以及永久更改系統重啓後保持不變的值。
# To display a list of available sysctl variables
sysctl -a | less
# To only list specific variables use
sysctl variable1 [variable2] [...]
# To change a value temporarily use the sysctl command with the -w option:
sysctl -w variable=value
# To override the value persistently, the /etc/sysctl.conf file must be changed. This is the recommend method. Edit the /etc/sysctl.conf file.
vi /etc/sysctl.conf
# Then add/change the value of the variable
variable = value
# Save the changes and close the file. Then use the -p option of the sysctl command to load the updated sysctl.conf settings:
sysctl -p or sysctl -p /etc/sysctl.conf
# The updated sysctl.conf values will now be applied when the system restarts.
2.2 NIC 環形緩衝區
-
首先,檢查步驟 (4) - NIC 環形緩衝區。它是一個循環緩衝區,大小固定,FIFO,位於 RAM 中。緩衝區可以順利接受大量連接而不丟棄它們,當您看到丟棄或超限時,您可能需要增加這些隊列,也就是說,傳入的數據包數量超過了內核能夠處理的數據包數量,副作用可能是延遲增加。
-
環形緩衝區的大小通常設置爲小於最大值。通常,增加接收緩衝區大小就足以防止丟包,因爲它可以讓內核有更多時間耗盡緩衝區。
ethtool -g eth3 Ring parameters for eth3: Pre-set maximums: RX: 8192 RX Mini: 0 RX Jumbo: 0 TX: 8192 Current hardware settings: RX: 1024 RX Mini: 0 RX Jumbo: 0 TX: 512 # eth3's inteface has the space for 8KB but only using 1KB
# Increase both the Rx and Tx buffers to the maximum ethtool -G eth3 rx 8192 tx 8192
ethtool -S eth3 | grep -e "err" -e "drop" -e "over" -e "miss" -e "timeout" -e "reset" -e "restar" -e "collis" -e "over" | grep -v "\: 0"
-
RHEL/CentOS:使用
/sbin/ifup-local
,詳情請參見此處。[28] -
Ubuntu:關注這裏 [29]
-
持久化更改:
-
如何監控:
-
更改命令:
-
檢查命令:
2.3 中斷合併 (IC) - rx-usecs、tx-usecs、rx-frames、tx-frames(硬件 IRQ)
-
繼續執行步驟 (5),硬中斷 - HardIRQ。NIC 排隊引用接收環形緩衝區隊列 rx 中的數據包,直到 rx-usecs 超時或 rx-frames,然後引發 HardIRQ。這稱爲中斷合併:
-
中斷過早:系統性能不佳(內核停止正在運行的任務來處理硬中斷)
-
中斷太晚:流量沒有及時從 NIC 上分離出來 -> 更多流量 -> 覆蓋 -> 流量丟失!
-
網絡將接收 / 傳輸的流量(幀數)
rx/tx-frames
,或接收 / 傳輸流量後經過的時間(超時)rx/tx-usecs
。 -
更新中斷合併可以減少 CPU 使用率、hardIRQ,可能會以延遲爲代價增加吞吐量
-
調優:
ethtool -c eth3 Coalesce parameters for eth3: Adaptive RX: on TX: off # Adaptive mdoe stats-block-usecs: 0 sample-interval: 0 pkt-rate-low: 400000 pkt-rate-high: 450000 rx-usecs: 16 rx-frames: 44 rx-usecs-irq: 0 rx-frames-irq: 0
# Turn adaptive mode off # Interrupt the kernel immediately upon reception of any traffic ethtool -C eth3 adaptive-rx off rx-usecs 0 rx-frames 0
-
如何監控:缺。
-
在中斷內核之前,至少允許一些數據包在 NIC 中緩衝,並至少允許一些時間通過。這些值取決於系統功能和收到的流量。
-
更改命令:
-
自適應模式使卡能夠自動調節 IC。驅動程序將檢查流量模式和內核接收模式,並實時估計合併設置,旨在防止數據包丟失 -> 如果收到許多小數據包,則很有用。
-
更高的中斷合併有利於帶寬而不是延遲:VOIP 應用程序(延遲敏感)可能比文件傳輸(吞吐量敏感)需要更少的合併
-
檢查命令:
2.4 IRQ 親和性
-
IRQ 具有關聯的 “親和屬性”,
smp_affinity
它定義了允許執行該 IRQ 的中斷服務例程 (ISR) 的 CPU 核心。此屬性可用於通過將中斷關聯和應用程序的線程關聯分配給一個或多個特定 CPU 核心來提高應用程序性能。這允許在指定的中斷和應用程序線程之間共享緩存行。 -
默認情況下,它由守護進程控制
irqbalancer
[30]。
systemctl status irqbalance.service
- 但如果需要,也可以手動平衡,以確定是否
irqbalance
未以最佳方式平衡 IRQ,從而導致數據包丟失。在某些非常特殊的情況下,永久手動平衡中斷可能會有所幫助。在進行此類調整之前,請確保停止irqbalance
:
systemctl stop irqbalance.service
-
特定 IRQ 號的中斷親和性值存儲在關聯
/proc/irq/<IRQ_NUMBER>/smp_affinity
文件中,root 用戶可以查看和修改該文件。存儲在此文件中的值是一個十六進制位掩碼,代表系統中的所有 CPU 核心。 -
要在具有 4 個核心的服務器上設置以太網驅動程序的中斷親和性(例如):
# Determine the IRQ number associated with the Ethernet driver
grep eth0 /proc/interrupts
32: 0 140 45 850264 PCI-MSI-edge eth0
# IRQ 32
# Check the current value
# The default value is 'f', meaning that the IRQ can be serviced
# on any of the CPUs
cat /proc/irq/32/smp_affinity
f
# CPU0 is the only CPU used
echo 1 > /proc/irq/32/smp_affinity
cat /proc/irq/32/smp_affinity
1
# Commas can be used to delimit smp_affinity values for discrete 32-bit groups
# This is required on systems with more than 32 cores
# For example, IRQ 40 is serviced on all cores of a 64-core system
cat /proc/irq/40/smp_affinity
ffffffff,ffffffff
# To service IRQ 40 on only the upper 32 cores
echo 0xffffffff,00000000 > /proc/irq/40/smp_affinity
cat /proc/irq/40/smp_affinity
ffffffff,00000000
-
用於在 Intel NIC 上設置 IRQ 親和性的腳本 [31],可處理具有 > 32 個核心的系統。
-
正如我所說,IRQ 親和性可以提高性能,但僅限於具有預定義工作負載的非常特定的配置。這是一把雙刃劍 [32]。
2.5 在 CPU 之間共享數據包處理負載
資料來源:
-
http://balodeamit.blogspot.com/2013/10/receive-side-scaling-and-receive-packet.html[33]
-
https://garycplin.blogspot.com/2017/06/linux-network-scaling-receives-packets.html[34]
-
https://github.com/torvalds/linux/blob/master/Documentation/networking/scaling.rst[35]
以前,一切都很簡單。網卡很慢,只有一個隊列。當數據包到達時,網卡通過 DMA 複製數據包併發出中斷,Linux 內核收穫這些數據包並完成中斷處理。隨着網卡速度越來越快,基於中斷的模型可能會因大量傳入數據包而導致 IRQ 風暴。這將消耗大部分 CPU 功率並凍結系統。爲了解決這個問題,提出了 NAPI[36](中斷和輪詢)。當內核從網卡接收到中斷時,它開始輪詢設備並儘快收穫隊列中的數據包。NAPI 與當今常見的 1Gbps 網卡配合良好。但是,對於 10Gbps、20Gbps 甚至 40Gbps 網卡,NAPI 可能不夠用。如果我們仍然使用一個 CPU 和一個隊列來接收數據包,這些卡將需要更快的 CPU。幸運的是,多核 CPU 現在很流行,那麼爲什麼不併行處理數據包呢?
2.5.1 接收端擴展 (RSS)
-
當數據包到達 NIC 時,它們會被添加到接收隊列。在設備驅動器初始化期間,接收隊列會被分配一個 IRQ 號,並且一個可用的 CPU 處理器會被分配給該接收隊列。該處理器負責爲 IRQ 提供中斷服務路由 (ISR)。通常,數據處理也由執行 ISR 的同一處理器完成。
-
eth1 接口有 4 個接收隊列和 4 個發送隊列,56-59 IRQ 被分配給這些隊列。現在數據包處理負載被分配到 4 個 CPU 上,從而實現更高的吞吐量和更低的延遲。
-
這些圖片來自 balodeamit 博客 [37]
-
IRQ 53 用於 “eth1-TxRx-0” 單聲道隊列。
-
檢查
smp_affinity
-> 隊列已配置爲將中斷髮送到 CPU8。
-
如果有大量網絡流量 -> 則只有單核負責處理數據。ISR 例程很小,因此如果在單核上執行,性能不會有很大差異,但數據處理和在 TCP/IP 堆棧中向上移動數據需要時間(其他核處於空閒狀態)。
-
RSS 來救場了!RSS 允許將網卡配置爲分佈在多個發送和接收隊列(環形緩衝區)中。這些隊列分別映射到每個 CPU 處理器。當每個隊列生成中斷時,它們將被髮送到映射的處理器 -> 網絡流量由多個處理器處理。
-
RSS 提供了在多處理環境中並行接收處理的好處。
-
這是 NIC 技術。它支持多個隊列,並在 NIC 中集成了哈希函數(按源和目標 IP 以及(如果適用)按 TCP/UDP 源和目標端口將數據包分發到不同的隊列)。NIC 爲每個傳入數據包計算一個哈希值。根據哈希值,NIC 將相同數據流的數據包分配給單個隊列,並在隊列之間均勻分配流量。
-
用命令檢查
ethool -L
。 -
根據 Linux 內核文檔 [38],
RSS should be enabled when latency is a concern or whenever receive interrupt processing froms a bottleneck... For low latency networking, the optimal setting is to allocate as many queues as there are CPUs in the system (or the NIC maximum, if lower)
。
- // WIP——命令!
2.5.2 接收數據包控制 (RPS)
-
從邏輯上講,RPS 是 RSS 的軟件實現。由於是軟件實現,因此它稍後會在數據路徑中被調用。RSS 選擇隊列,從而選擇運行硬件中斷處理程序的 CPU,而 RPS 選擇 CPU 在中斷處理程序之上執行協議處理。
-
當驅動程序接收到一個數據包時,它會將數據包包裝在一個套接字緩衝區中
sk_buff
,該緩衝區包含u32
該數據包的哈希值(基於源 IP、源端口、目標 IP 和目標端口)。由於同一 TCP/UDP 連接(流)的每個數據包共享相同的哈希值,因此使用同一個 CPU 來處理它們是合理的。之後,它將到達netif_rx_internal()
或netif_receive_skb_internal()
,然後get_rps_cpu()
將被調用將哈希映射到 中的條目rps_map
,即 CPU id。獲取 CPU id 後,enqueue_to_backlog()
將 sk_buff 放入特定的 CPU 隊列以 進行進一步處理。每個 CPU 的隊列都分配在每個 CPU 變量中softnet_data
[39]。
-
使用 RPS 的好處與 RSS 相同:在 CPU 之間共享數據包處理的負載。
-
如果有 RSS 則可能不需要。
-
如果 CPU 數量多於隊列數量,RPS 仍然有用。
-
RPS 需要使用 kconfig 符號編譯的內核
CONFIG_RPS
(默認情況下,SMP 啓用該符號)。即使編譯後,RPS 仍處於禁用狀態,直到明確配置爲止。可以使用 sysfs 文件條目爲每個接收隊列配置 RPS 可以轉發流量的 CPU 列表:
/sys/class/net/<dev>/queues/rx-<n>/rps_cpus
# This file implements a bitmap of CPUs
# 0 (default): disabled
-
建議配置:
-
單隊列設備:
rps_cpus
- 與中斷 CPU 位於同一內存域中的 CPU。如果 NUMA 局部性不是問題,rps_cpus
- 系統中的所有 CPU。在高中斷率下,將中斷 CPU 從映射中排除可能是明智之舉,因爲它已經執行了大量工作。 -
多隊列系統:如果配置了 RSS -> RPS 是多餘的且不必要的。如果硬件隊列比 CPU 少,那麼如果
rps_cpus
每個隊列都與該隊列的中斷 CPU 共享相同的內存域,則 RPS 可能會有益。
2.5.3 接收流控制 (RFS)
-
雖然 RPS 根據流量分發數據包,但它並沒有考慮用戶空間應用程序。
-
應用程序可能在 CPU A 上運行,內核將數據包放在 CPU B 的隊列中。
-
CPU A 只能使用自己的緩存,CPU B 中緩存的數據包變得毫無用處。
-
RFS 爲應用程序進一步擴展了 RPS。
-
CONFIG_RPS
僅當啓用 kconfig 符號時,RFS 纔可用。 -
RFS 並不維護每個隊列的哈希到 CPU 映射,而是維護一個全局的流到 CPU 表。
rps_sock_flow_table
該表的大小可以調整:
sysctl -w net.core.rps_sock_flow_entries 32768
-
雖然套接字流表改善了應用程序的局部性,但它也帶來了一個問題。當調度程序將應用程序遷移到新 CPU 時,舊 CPU 隊列中剩餘的數據包將變爲未完成的,應用程序可能會收到無序的數據包。爲了解決這個問題,RFS 使用每個隊列
rps_dev_flow_table
來跟蹤未完成的數據包。 -
每個隊列流表的大小
rps_dev_flow_table
可以通過 sysfs 接口配置:/sys/class/net/<dev>/queues/rx-<n>/rps_flow_cnt.
-
接下來的步驟太複雜了,如果你想知道,請查看這個 [40]。
-
建議配置:
-
建議的流量數取決於任何給定時間的預期活動連接數,該數可能明顯少於連接數 -
32768
>rps_sock_flow_entries
。 -
單隊列設備:
rps_flow_cnt
=rps_sock_flow_entries
。 -
多隊列設備:(
rps_flow_cnt
每個隊列)=rps_sock_flow_entries / N
(N 爲隊列數)。
2.5.4 加速接收流控制 (aRFS)
-
加速 RFS 之於 RFS 就像 RSS 之於 RPS:一種硬件加速的負載平衡機制,它使用軟狀態根據使用每個流的數據包的應用程序的運行位置來引導流。
-
aRFS 的性能應該比 RFS 更好,因爲數據包被直接發送到使用數據的線程本地的 CPU。
-
僅當滿足以下條件時,aRFS 纔可用:
-
aRFS 必須由網絡接口 卡支持(導出
ndo_rx_flow_steer
netdevice 函數) -
ntuple
必須啓用過濾。 -
內核是用 編譯的
CONFIG_RFS_ACCEL
。 -
CPU 到隊列的映射是根據驅動程序爲每個接收隊列配置的 IRQ 親和性自動推斷出來的,因此不需要額外的配置。
-
建議配置:
-
只要想要使用 RFS 並且 NIC 支持硬件加速,即可啓用。
2.6 中斷合併(軟 IRQ)
-
net.core.netdev_budget_usecs
: -
更改命令:
-
保留該值,檢查此項 [41]
-
調優:
sysctl -w net.core.netdev_budget_usecs <value>
-
net.core.netdev_budget
: -
更改命令:
-
保留該值,檢查此項 [42]
-
如何監控:
-
如果第一列以外的任何一列都在增加,則需要更改預算。小幅增加是正常的,不需要調整。
-
調優:
sysctl -w net.core.netdev_budget <value>
cat /proc/net/softnet_stat
-
net.core.dev_weight
: -
更改命令:
-
保留該值,檢查此項 [43]
-
如何監控:
-
調優:
sysctl -w net.core.dev_weight <value>
cat /proc/net/softnet_stat
2.7 接收 QDisc
-
在步驟 (14) 中,我提到了
netdev_max_backlog
,這是關於每個 CPU 的積壓隊列。netif_receive_skb()
內核函數(步驟 (12))將找到數據包對應的 CPU,並將數據包排入該 CPU 的隊列。如果該處理器的隊列已滿且已達到最大大小,則數據包將被丟棄。隊列的默認大小 -netdev_max_backlog
值爲 1000,這可能不足以用於以 1Gbps 運行的多個接口,甚至以 10Gbps 運行的單個接口。 -
調優:
cat /proc/net/softnet_stat
-
第二列是當 netdev 積壓隊列溢出時遞增的計數器。
-
將值加倍 -> 檢查
/proc/net/softnet_stat
-
如果利率降低 -> 價值加倍
-
重複,直到確定最佳尺寸並且液滴不再增加
-
更改命令:
sysctl -w net.core.netdev_max_backlog <value>
-
保留該值,檢查此項 [44]
-
如何監控:確定積壓是否需要增加。
2.8 發送 QDisc - txqueuelen 和 default_qdisc
-
在步驟(11)(傳輸)中,有
txqueuelen
一個隊列 / 緩衝區來面對連接緩衝區並應用流量控制(tc)[45]。 -
調優:
ifconfig <interface> txqueuelen value
ip -s link # Check RX/TX dropped?
-
如何監控:
-
更改命令:
-
您
default_qdisc
也可以進行更改,因爲每個應用程序都有不同的負載並且需要流量控制,並且它也用於對抗緩衝區浮動 [46]。可以查看本文 - 隊列學科部分 [47]。 -
調優:
sysctl -w net.core.default_qdisc <value> `` - 保留該值,檢查[此項](https://access.redhat.com/discussions/2944681 "此項") - 如何監控: ```shell tc -s qdisc ls dev <interface> # Example qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64 Sent 33867757 bytes 231578 pkt (dropped 0, overlimits 0 requeues 6) # Dropped, overlimits, requeues!!! backlog 0b 0p requeues 6 maxpacket 5411 drop_overlimit 0 new_flow_count 1491 ecn_mark 0 new_flows_len 0 old_flows_len 0
-
更改命令:
### 2.9 TCP 讀寫緩衝區/隊列
- 定義在`tcp_mem`和 `tcp_moderate_rcvbuf`時指定的[內存壓力](https://wwwx.cs.unc.edu/~sparkst/howto/network_tuning.php "內存壓力")是多少。
- 我們可以調整緩衝區的 mix-max 大小來提高性能:
- 更改命令:
```shell
sysctl -w net.ipv4.tcp_rmem="min default max"
sysctl -w net.ipv4.tcp_wmem="min default max"
```
- 保留該值,檢查[此項](https://access.redhat.com/discussions/2944681 "此項")
- 如何監控:檢查`/proc/net/sockstat`。
### 2.10 TCP FSM 和擁塞算法
> Accept 和 SYN 隊列由 net.core.somaxconn 和 net.ipv4.tcp_max_syn_backlog 控制。[現在 net.core.somaxconn 限制了這兩個隊列的大小](https://blog.cloudflare.com/syn-packet-handling-in-the-wild/#queuesizelimits "現在 net.core.somaxconn 限制了這兩個隊列的大小")。
- `net.core.somaxconn`:爲傳遞給 的 backlog 參數的值提供上限`listen() function`,在用戶空間中稱爲`SOMAXCONN`。如果您更改此值,您還應將應用程序更改爲兼容的值。您可以查看[Envoy 的性能調優說明](https://ntk148v.github.io/posts/til/envoy/performance.md "Envoy 的性能調優說明")。
- `net.ipv4.tcp_fin_timeout`:指定在強制關閉套接字之前等待最終 FIN 數據包的秒數。
- `net.ipv4.tcp_available_congestion_control`:顯示已註冊的可用擁塞控制選項。
- `net.ipv4.tcp_congestion_control`:設置用於新連接的擁塞控制算法。
- `net.ipv4.tcp_max_syn_backlog`:設置尚未收到連接客戶端確認的排隊連接請求的最大數量;如果超過此數字,內核將開始丟棄請求。
- `net.ipv4.tcp_syncookies`:啓用/禁用 syn cookie,用於防止 syn flood 攻擊。
- `net.ipv4.tcp_slow_start_after_idle`:啓用/禁用 tcp 慢啓動。

- 您可能想要檢查[寬帶調整說明](https://ntk148v.github.io/posts/linux-network-performance-ultimate-guide/broadband-tweaks.md "寬帶調整說明")。
### 2.11 NUMA
- 這個術語超出了網絡性能的範疇。
- 非均勻內存訪問 (NUMA) 是一種內存架構,它允許處理器比其他傳統技術更快地訪問內存內容。換句話說,處理器訪問本地內存的速度比訪問非本地內存快得多。這是因爲在 NUMA 設置中,每個處理器都被分配了一個專門供自己使用的特定本地內存。這消除了非本地內存的共享,減少了當多個請求訪問同一內存位置時的延遲(更少的內存鎖)-> 提高網絡性能(因爲 CPU 必須訪問環形緩衝區(內存)來處理數據包)

- NUMA 架構將 CPU、內存和設備的子集拆分爲不同的“節點”,實際上創建了具有快速互連和通用操作系統的多臺小型計算機。NUMA 系統需要與非 NUMA 系統進行不同的調整。對於 NUMA,其目標是將單個節點中設備的所有中斷分組到屬於該節點的 CPU 核心上。
- 儘管這看起來好像有助於減少延遲,但衆所周知,NUMA 系統與實時應用程序的交互很差,因爲它們可能導致意外事件延遲。
- 確定 NUMA 節點:
```shell
ls -ld /sys/devices/system/node/node*
drwxr-xr-x. 3 root root 0 Aug 15 19:44 /sys/devices/system/node/node0
drwxr-xr-x. 3 root root 0 Aug 15 19:44 /sys/devices/system/node/node1
- 確定 NUMA 位置:
cat /sys/devices/system/node/node0/cpulist
0-5
cat /sys/devices/system/node/node1/cpulist
# empty
- 調整所有 CPU 的 IRQ 親和性是有意義的,請確保您使用 sudo systop
irqbalance
服務並手動設置 CPU 親和性:
systemctl stop irqbalance
-
確定設備位置:
# cat /sys/class/net/<interface>/device/numa_node cat /sys/class/net/eth3/device/numa_node 1 # -1 - the hardware platform is not actually NUMA and the kernel is just emulating # or 'faking' NUMA, or a device is on a bus which does not have any NUMA locality, # such as a PCI package
-
檢查 PCIe 網絡接口是否屬於特定 NUMA 節點。該命令將顯示 NUMA 節點號,設備的中斷應定向到 PCIe 設備所屬的 NUMA 節點
-
Linux 內核從 2.5 版本開始支持 NUMA - RedHat、基於 Debian 的內核通過兩個軟件包
numactl
和提供 NUMA 支持以實現進程優化numad
。systemctl enable numad systemctl start numad
-
numadctl
:控制進程或共享內存的 NUMA 策略。 -
numad
是一個守護進程,可幫助在具有 NUMA 架構的系統上進行進程和內存管理。Numad 通過監視系統拓撲和資源使用情況來實現此目的,然後嘗試定位進程以實現高效的 NUMA 局部性和效率,其中進程哈希值足夠大內存大小和 CPU 負載。
2.12 更多內容——數據包處理
本節爲高級部分,介紹了一些實現高性能的高級模塊 / 框架。
2.12.1AF_PACKET
v4
資料來源:
-
https://developer.ibm.com/articles/j-zerocopy/[48]
-
https://lwn.net/Articles/737947/d[49]
-
Linux 中的新快速數據包接口:
-
爲了更好地理解問題的解決方案,我們首先需要了解問題本身。
-
此示例取自 IBM 文章 [51]。
-
場景:從文件讀取並通過網絡將數據傳輸到另一個程序。
-
複製操作需要在用戶態和內核態進行 4 次上下文切換,數據被複制了 4 次纔算操作完成。
-
零拷貝通過消除這些冗餘的數據拷貝來提高性能。
-
您會注意到,實際上不需要第 2 和第 3 次複製(應用程序除了緩存數據並將其傳輸回套接字緩衝區外不執行任何其他操作)-> 數據可以直接從讀取緩衝區傳輸到套接字緩衝區 -> 使用方法
transferTo()
,假設此方法將數據從文件通道傳輸到給定的可寫字節通道。在內部,它取決於操作系統對零複製的支持(在 Linux、UNIX 中,這是 sissendfile()
系統調用)。 -
硬件描述符僅映射到內核
-
AF_PACKET v4
-
數據路徑中沒有系統調用
-
默認複製模式
-
真正的零拷貝 [50] 模式
PACKET_ZEROCOPY
,DMA 數據包緩衝區映射到用戶空間。File.read(fileDesc, buf, len); Socket.send(socket, buf, len);
#include <sys/socket.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
public void transferTo(long position, long count, WritableByteChannel target); // Copy data from a disk file to a socket transferTo(position, count, writableChannel);
-
收集操作:在 Linux 內核 2.4 及更高版本中,套接字緩衝區描述符已修改以滿足此要求。這種方法不僅減少了多次上下文切換,還消除了需要 CPU 參與的重複數據複製。
-
不會將任何數據複製到套接字緩衝區中。相反,只有包含數據位置和長度信息的描述符纔會附加到套接字緩衝區中。DMA 引擎將數據直接從內核緩衝區傳遞到協議引擎,從而消除了剩餘的最終 CPU 複製。
-
爲了提高 Rx 和 Tx 性能,此實現使用了
PACKET_MMAP
。
5.12.2PACKET_MMAP
資料來源:
-
https://docs.kernel.org/networking/packet_mmap.html[52]
-
PACKET_MMAP
是一個用於快速數據包嗅探的 Linux API。 -
它提供了一個映射環形緩衝區,由用戶空間和內核共享,用於發送和接收數據包。這有助於減少系統調用以及用戶空間和內核之間所需的複製。
5.12.3 繞過內核:數據平面開發套件(DPDK)
資料來源:
-
https://blog.cloudflare.com/kernel-bypass/[53]
-
https://www.cse.iitb.ac.in/~mythili/os/anno_slides/network_stack_kernel_bypass_slides.pdf[54]
-
https://selectel.ru/blog/en/2016/11/24/introduction-dpdk-architecture-principles/[55]
-
https://www.slideshare.net/garyachy/dpdk-44585840[56]
-
內核不足:
-
用戶空間數據包處理:
-
用戶空間網絡棧
-
數據平面開發套件(DPDK)
-
etmap
-
…
-
mTCP
-
…
-
內核和用戶空間之間的上下文切換
-
內核和用戶空間之間的數據包複製
-
動態分配
sk_buff
-
每包中斷
-
共享數據結構
-
要了解該問題,請查看此幻燈片 [57]。
-
內核堆棧中的性能開銷:
-
解決方案:爲什麼只是繞過 內核?
-
內核旁路技術有很多種:
-
但我只談論 DPDK,因爲它最受歡迎。
-
DPDK(數據平面開發套件):
-
嚴重依賴硬件。
-
必須專用一個 CPU 核心並將其分配給運行 PMD。100% CPU。
-
核心組件:
-
輪詢模式驅動程序:當收到幀時,NIC 不會向 CPU 發出中斷,而是 CPU 運行輪詢模式驅動程序 (PMD) 來不斷輪詢 NIC 是否有新數據包。但是,這意味着必須有一個專用的 CPU 核心分配給正在運行的 PMD。但是,這意味着必須有一個專用的 CPU 核心分配給正在運行的 PMD。DPDK 包括用於 1 GbE、10 GbE 和 40GbE 的輪詢模式驅動程序 (PMD),以及半虛擬化 virtio 以太網控制器,這些控制器設計爲無需異步、基於中斷的信號機制即可工作。
-
數據包轉發算法支持:DPDK 包含 Hash(
librte_hash
)和最長前綴匹配(LPM,librte_lpm
)庫來支持相應的數據包轉發算法。 -
librte_net
:IP 協議定義和便利宏的集合。它基於 FreeBSD* IP 堆棧的代碼,包含協議編號(用於 IP 標頭)、IP 相關宏、IPv4/IPv6 標頭結構以及 TCP、UDP 和 SCTP 標頭結構。 -
mbuf 庫提供了創建和銷燬緩衝區的功能,DPDK 應用程序可以使用它來存儲消息緩衝區(在啓動時創建並存儲在內存池中)
-
提供 API 來分配 / 釋放 mbufs,操作用於承載網絡數據包的數據包緩衝區。
-
池通過名稱來標識,並使用環來存儲空閒對象。
-
提供一些可選服務,例如每個核心的對象緩存和對齊助手,以確保對象被填充以在所有 RAM 通道上均勻分佈。
-
環境抽象層 (EAL):提供一個通用接口,嚮應用程序和庫隱藏環境細節。
-
環管理器(
librte_ring
):環結構在有限大小的表中提供無鎖的多生產者、多消費者 FIFO API。 -
內存池管理器(
librte_mempool
):負責在內存中分配對象池。 -
網絡數據包緩衝區管理(
librte_mbuf
): -
計時器管理器(
librte_timer
):爲 DPDK 執行單元提供計時器服務,提供異步執行函數的能力。 -
內核根本不介入:與網卡的交互是通過特殊的驅動程序和庫進行的
-
需要將網卡上接收傳入流量的端口與 Linux(內核驅動程序)解除綁定。這是使用
dpdk_nic_bind
(或dpkg-devbind
)命令完成的,./dpdk_nic_bind.py
在早期版本中爲 。 -
那麼 DPDK 如何管理端口呢?Linux 中的每個驅動程序都有綁定和取消綁定文件:
-
要將設備與驅動程序解除綁定,需要將設備的總線號寫入解除綁定文件。同樣,要將設備綁定到另一個驅動程序,需要將總線號寫入其綁定文件。有關此內容的更多詳細信息,請參見此處 [58]。
-
DPDK 安裝說明告訴我們端口 [59] 需要由 vfio_pci、igb_uio 或 uio_pci_generic 驅動程序管理。
-
這些驅動程序使得與用戶空間中的設備交互成爲可能。當然它們包含一個內核模塊,但那只是初始化設備和分配 PCI 接口。
-
應用程序和網卡之間的所有進一步通信均由 DPDK PMD 組織。
-
DPDK 還需要配置大頁面。這是分配大塊內存並向其中寫入數據所必需的(與 DPDK 在傳統數據包處理中所做的工作相同)
-
主階段:
-
傳入的數據包進入環形緩衝區。應用程序定期檢查此緩衝區是否有新數據包
-
如果緩衝區包含新的數據包描述符,應用程序將使用數據包描述符中的指針引用專門分配的內存池中的 DPDK 數據包緩衝區。
-
如果環形緩衝區不包含任何數據包,應用程序將把網絡設備排隊到 DPDK 下,然後再次引用環。
-
當 NIC 由 DPDK 驅動程序控制時,它對內核是不可見的
-
10 或 40Gb NIC
-
速度是最重要的標準
-
僅轉發數據包——而不是網絡堆棧
-
由各種用戶空間庫和驅動程序組成的框架快速數據包處理。
-
目標:以本機速度(快速數據包處理)將網絡數據包轉發到 / 從網絡接口卡(NIC)到用戶應用程序。
-
所有流量都會繞過內核:
-
開源(大部分爲 BSD-3,Linux 內核相關部分爲 GPL)
-
工作原理:
ls /sys/bus/pci/drivers/ixgbe bind module new_id remove_id uevent unbind
-
組件:
-
限制:
2.12.4 PF_RING
資料來源:
-
https://www.ntop.org/products/packet-capture/pf_ring/[60]
-
https://repository.ellak.gr/ellak/bitstream/11087/1537/1/5-deri-high-speed-network-analysis.pdf[61]
-
https://www.synacktiv.com/en/publications/writing-namespace-isolation-with-pf_ring-before-700.html[62]
-
PF_RING[63] 是一個 Linux 內核模塊和用戶空間框架,允許您以高速率處理數據包,同時爲數據包處理應用程序提供一致的 API。
-
PF_RING
通過 Linux NAPI 輪詢來自 NIC 的數據包。這意味着 NAPI 將數據包從 NIC 複製到PF_RING
循環緩衝區,然後用戶空間應用程序從環中讀取數據包。在這種情況下,有 2 個輪詢器,應用程序和 NAPI 都存在,這導致 CPU 內核用於此輪詢 -> 優點:PF_RING
可以同時將傳入的數據包分發到多個環。
-
PF_RING
具有模塊化架構,可以使用除標準PF_RING
模塊之外的附加組件。 - ZC 模塊(零拷貝): - 基於 FPGA 的卡模塊:增加對多家供應商的支持 - Stack 模塊:可用於向 Linux 網絡堆棧注入數據包 - 時間線模塊:可用於無縫提取 n2disk 轉儲集中的流量PF_RING API
- Sysdig 模塊:使用 sysdig 內核模塊捕獲系統事件 -
好處:
-
它爲傳入的數據包創建一條直線路徑,以使它們成爲一等公民
-
無需使用自定義網卡:任何卡都受支持
-
對應用程序透明:舊版應用程序需要重新編譯才能使用
-
無需內核或低級編程
-
熟悉網絡應用程序的開發人員可以立即利用它,而無需學習新的 API
-
PF_RING
降低了數據包捕獲和轉發到用戶空間的成本。但是,它有一些設計限制,因爲它需要兩個參與者來捕獲數據包,從而導致性能不佳: -
內核:將數據包從 NIC 複製到環
-
用戶空間:從環中讀取數據包並處理它
-
PF_RING
自 7.5 版本包含對 [64]AF_XDP
適配器的支持以來,從源代碼編譯時默認啓用此功能。
2.12.5 可編程數據包處理:eXpress 數據路徑 (XDP)
資料來源:
-
https://www.iovisor.org/technology/xdp[65]
-
https://blogs.igalia.com/dpino/2019/01/10/the-express-data-path/[66]
-
https://pantheon.tech/what-is-af_xdp/[67]
-
https://github.com/iovisor/bpf-docs/blob/master/Express_Data_Path.pdf[68]
-
https://github.com/xdp-project/xdp-paper/blob/master/xdp-the-express-data-path.pdf[69]
-
http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf[70]
-
https://arthurchiao.art/blog/firewalling-with-bpf-xdp/[71]
-
https://archive.fosdem.org/2018/schedule/event/xdp/attachments/slides/2220/export/events/attachments/xdp/slides/2220/fosdem18_SdN_NFV_qmonnet_XDPoffload.pdf[72]
-
https://people.netfilter.org/hawk/presentations/KernelRecipes2018/XDP_Kernel_Recipes_2018.pdf[73]
-
https://legacy.netdevconf.info/2.1/session.html?gospodarek[74]
-
XDP(eXpress 數據路徑):
-
XDP 專爲高性能而設計
-
… 和可編程性:無需修改內核即可即時實現新功能
-
XDP 不是內核旁路:
-
XDP 不會取代 TCP/IP 堆棧。
-
XDP 不需要任何專門的硬件,但有一些硬件要求:
-
它是內核堆棧中的集成快速路徑。
-
如果說傳統的內核網絡堆棧是一條高速公路,那麼內核旁路就是建設高速列車基礎設施的提議,而 XDP 就是在高速公路上增加拼車車道的提議——Tom Herbert 和 Alexei Starovoitov。
-
TX/RX 校驗和卸載
-
接收方縮放 (RSS)
-
傳輸分段卸載 (TSO)
-
多隊列 NIC
-
常見協議通用卸載:
-
LRO、aRFS、設備流哈希是 “不錯的選擇”
-
通過 filter 等 pre-stack 處理來緩解 DOS 攻擊
-
轉發和負載平衡
-
批處理技術,例如通用接收卸載 (GRO)
-
流量採樣、監控
-
ULP 處理
-
分配 SKB 之前
-
設備驅動程序內部 RX 函數
-
直接在 RX 數據包頁面上操作
-
eBPF 是內核執行的用戶定義、沙盒字節碼。更多信息請查看 [75]。
-
從以前的 BPF 版本 (cBPF,由 tcpdump 使用) 演變而來
-
11 個寄存器(64 位)、512 字節堆棧
-
對上下文的讀寫訪問(用於網絡:數據包)
-
LLVM 後端從 c 編譯爲 eBPF(或從 Lua、go、P4、Rust 等)
-
內核驗證器確保安全
-
適用於主要架構的 JIT(即時)編譯器
-
特性:
-
映射:eBPF 程序之間或與用戶空間共享的鍵值條目(哈希、數組等)
-
尾調用:從一個程序 “長跳轉” 到另一個程序,上下文被保留
-
編程助手函數;從 eBPF 程序調用的內核函數白名單:獲取當前時間、打印調試信息、查找或更新地圖、縮小或增加數據包……
-
用於早期數據包攔截的 eBPF 實現。它是 Linux 網絡數據路徑中的可編程、高性能、專用應用程序數據包處理器。
-
在 SW 網絡棧的最低點進行裸數據包處理。
-
使用案例:
-
特性:
-
與 DPDK 比較:
-
允許選擇忙輪詢或中斷驅動網絡
-
無需分配大頁面
-
無特殊硬件要求
-
不需要專用 CPU,用戶可以選擇如何在 CPU 之間構建工作
-
無需從第三方用戶空間應用程序向內核注入數據包
-
無需爲訪問網絡硬件定義新的安全模型
-
無需第三方代碼 / 許可。
-
XDP 是一個年輕的項目,但非常有前景。
-
XDP 相對於 DPDK 的優勢:
-
XDP 數據包處理器:
-
Forward:
-
Drop:
-
Normal receive:
-
GRO:
-
可能是數據包修改後
-
TX 隊列專屬於同一 CPU,因此無需鎖定
-
只需從函數返回錯誤
-
驅動程序回收頁面
-
分配 skbuff 並接收到堆棧中
-
將數據包發送到另一個 CPU 進行處理
-
允許用戶空間使用 “原始” 接口
AF_PACKET
,如 netmap -
合併同一連接的數據包
-
執行大數據包的接收
-
解析數據包
-
執行表查找,創建 / 管理狀態過濾器
-
處理數據包
-
返回操作:
-
無鎖定 RX 隊列
-
CPU 可以專用於忙輪詢以使用中斷模型
-
功能接口
-
沒有提前分配 skbuff,沒有 SW 隊列
-
在內核中
-
處理 RX 數據包的組件
-
直接從驅動程序處理 RX“數據包頁面”
-
爲每個 RX 隊列分配一個 CPU
-
BPF 程序執行處理
-
基本動作:
-
AF_XDP
: -
升級版
AF_PACKET
:使用 XDP 程序觸發選定隊列的 Rx 路徑 -
XDP 程序可以通過 eBPF 將幀重定向到用戶空間中的內存緩衝區 -> 不繞過內核而是創建內核快速路徑。
-
DMA 傳輸使用用戶空間內存(零拷貝)
-
好處:
-
DPDK 應用程序無需更改,內核驅動程序負責處理硬件
-
爲用戶提供新的選擇
-
處理數據包的 eBPF 程序可以以非常有效的方式轉發給應用程序
-
用戶空間與內核空間之間的零拷貝
-
相比之下,實現了 3-20 倍的改進
AF_PACKET
-
性能改進:
-
將 XDP 直通直接連接到用戶空間:
-
對於 DPDK[77]:
-
Linux 4.18[76] 中引入了一種新型套接字,它並不完全繞過內核,而是利用其功能並能夠創建類似於 DPDK 或的東西
AF_PACKET
。 -
限制:
-
相當年輕的項目
-
需要新的內核版本(>=5.4)才能完全支持
“
本文在 AI 的幫助下進行了翻譯,如果有些術語沒有糾正,歡迎在評論區指正
參考資料
[1]
Linux Network Performance Ultimate Guide:https://ntk148v.github.io/posts/linux-network-performance-ultimate-guide/
[2]
https://github.com/leandromoreira/linux-network-performance-parameters/:https://github.com/leandromoreira/linux-network-performance-parameters/
[3]
https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf:https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf
[4]
https://www.coverfire.com/articles/queueing-in-the-linux-network-stack/:https://www.coverfire.com/articles/queueing-in-the-linux-network-stack/
[5]
https://blog.cloudflare.com/how-to-achieve-low-latency/:https://blog.cloudflare.com/how-to-achieve-low-latency/
[6]
https://blog.cloudflare.com/how-to-receive-a-million-packets/:https://blog.cloudflare.com/how-to-receive-a-million-packets/
[7]
https://beej.us/guide/bgnet/html/:https://beej.us/guide/bgnet/html/
[8]
https://blog.csdn.net/armlinuxww/article/details/111930788:https://blog.csdn.net/armlinuxww/article/details/111930788
[9]
https://www.ibm.com/docs/en/linux-on-systems?topic=recommendations-network-performance-tuning:https://www.ibm.com/docs/en/linux-on-systems?topic=recommendations-network-performance-tuning
[10]
https://blog.packagecloud.io/illusterated-guide-monitoring-tuning-linux-networking-stack-receiving-data/:https://blog.packagecloud.io/illustrated-guide-monitoring-tuning-linux-networking-stack-receiving-data/
[11]
https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-receiving-data/:https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-receiving-data/
[12]
https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-sending-data/:https://blog.packagecloud.io/monitoring-tuning-linux-networking-stack-sending-data/
[13]
https://www.sobyte.net/post/2022-10/linux-net-snd-rcv/:https://www.sobyte.net/post/2022-10/linux-net-snd-rcv/
[14]
https://juejin.cn/post/7106345054368694280:https://juejin.cn/post/7106345054368694280
[15]
https://openwrt.org/docs/guide-developer/networking/praxis:https://openwrt.org/docs/guide-developer/networking/praxis
[16]
https://blog.51cto.com/u_15169172/2710604:https://blog.51cto.com/u_15169172/2710604
[17]
https://sn0rt.github.io/media/paper/TCPlinux.pdf:https://sn0rt.github.io/media/paper/TCPlinux.pdf
[18]
https://medium.com/coccoc-engineering-blog/linux-network-ring-buffers-cea7ead0b8e8:https://medium.com/coccoc-engineering-blog/linux-network-ring-buffers-cea7ead0b8e8
[19]
HOWTO#sysctl 部分:https://ntk148v.github.io/posts/linux-network-performance-ultimate-guide/#204-sysctl
[20]
PackageCloud 的文章:https://blog.packagecloud.io/illustrated-guide-monitoring-tuning-linux-networking-stack-receiving-data
[21]
混雜模式:https://unix.stackexchange.com/questions/14056/what-is-kernel-ip-forwarding
[22]
DMA(直接內存訪問):https://en.wikipedia.org/wiki/Direct_memory_access
[23]
SKB:http://vger.kernel.org/~davem/skb.html
[24]
循環緩衝區:https://en.wikipedia.org/wiki/Circular_buffer
[25]
NAPI:https://en.wikipedia.org/wiki/New_API
[26]
優先:https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_output.c#L241
[27]
請點擊此處:https://unix.stackexchange.com/questions/419518/how-to-tell-how-much-memory-tcp-buffers-are-actually-using
[28]
此處。:https://access.redhat.com/solutions/8694
[29]
這裏:https://unix.stackexchange.com/questions/542546/what-is-the-systemd-native-way-to-manage-nic-ring-buffer-sizes-before-bonded-int
[30]
irqbalancer
:https://github.com/Irqbalance/irqbalance
[31]
用於在 Intel NIC 上設置 IRQ 親和性的腳本:https://gist.github.com/xdel/9c50ccedea9e0c9d0000d550b07ee242
[32]
一把雙刃劍:https://stackoverflow.com/questions/48659720/is-it-a-good-practice-to-set-interrupt-affinity-and-io-handling-thread-affinity
[33]
http://balodeamit.blogspot.com/2013/10/receive-side-scaling-and-receive-packet.html:http://balodeamit.blogspot.com/2013/10/receive-side-scaling-and-receive-packet.html
[34]
https://garycplin.blogspot.com/2017/06/linux-network-scaling-receives-packets.html:https://garycplin.blogspot.com/2017/06/linux-network-scaling-receives-packets.html
[35]
https://github.com/torvalds/linux/blob/master/Documentation/networking/scaling.rst:https://github.com/torvalds/linux/blob/master/Documentation/networking/scaling.rst
[36]
NAPI:https://wiki.linuxfoundation.org/networking/napi
[37]
balodeamit 博客:http://balodeamit.blogspot.com/2013/10/receive-side-scaling-and-receive-packet.html
[38]
Linux 內核文檔:https://github.com/torvalds/linux/blob/v4.11/Documentation/networking/scaling.txt#L80
[39]
softnet_data
:https://github.com/torvalds/linux/blob/v4.11/include/linux/netdevice.h#L2788
[40]
這個:https://garycplin.blogspot.com/2017/06/linux-network-scaling-receives-packets.html
[41]
此項:https://access.redhat.com/discussions/2944681
[42]
此項:https://access.redhat.com/discussions/2944681
[43]
此項:https://access.redhat.com/discussions/2944681
[44]
此項:https://access.redhat.com/discussions/2944681
[45]
流量控制(tc):http://tldp.org/HOWTO/Traffic-Control-HOWTO/intro.html
[46]
緩衝區浮動:https://www.bufferbloat.net/projects/codel/wiki/
[47]
本文 - 隊列學科部分:https://www.coverfire.com/articles/queueing-in-the-linux-network-stack/
[48]
https://developer.ibm.com/articles/j-zerocopy/:https://developer.ibm.com/articles/j-zerocopy/
[49]
https://lwn.net/Articles/737947/d:https://lwn.net/Articles/737947/d
[50]
零拷貝:https://en.wikipedia.org/wiki/Zero-copy
[51]
IBM 文章:https://developer.ibm.com/articles/j-zerocopy/
[52]
https://docs.kernel.org/networking/packet_mmap.html:https://docs.kernel.org/networking/packet_mmap.html
[53]
https://blog.cloudflare.com/kernel-bypass/:https://blog.cloudflare.com/kernel-bypass/
[54]
https://www.cse.iitb.ac.in/~mythili/os/anno_slides/network_stack_kernel_bypass_slides.pdf:https://www.cse.iitb.ac.in/~mythili/os/anno_slides/network_stack_kernel_bypass_slides.pdf
[55]
https://selectel.ru/blog/en/2016/11/24/introduction-dpdk-architecture-principles/:https://selectel.ru/blog/en/2016/11/24/introduction-dpdk-architecture-principles/
[56]
https://www.slideshare.net/garyachy/dpdk-44585840:https://www.slideshare.net/garyachy/dpdk-44585840
[57]
幻燈片:https://www.cse.iitb.ac.in/~mythili/os/anno_slides/network_stack_kernel_bypass_slides.pdf
[58]
此處:https://lwn.net/Articles/143397/
[59]
告訴我們端口:http://dpdk.org/doc/guides-16.04/linux_gsg/build_dpdk.html#loading-modules-to-enable-userspace-io-for-dpdk
[60]
https://www.ntop.org/products/packet-capture/pf_ring/:https://www.ntop.org/products/packet-capture/pf_ring/
[61]
https://repository.ellak.gr/ellak/bitstream/11087/1537/1/5-deri-high-speed-network-analysis.pdf:https://repository.ellak.gr/ellak/bitstream/11087/1537/1/5-deri-high-speed-network-analysis.pdf
[62]
https://www.synacktiv.com/en/publications/writing-namespace-isolation-with-pf_ring-before-700.html:https://www.synacktiv.com/en/publications/breaking-namespace-isolation-with-pf_ring-before-700.html
[63]
PF_RING:https://github.com/ntop/PF_RING
[64]
自 7.5 版本包含對:https://www.ntop.org/guides/pf_ring/modules/af_xdp.html
[65]
https://www.iovisor.org/technology/xdp:https://www.iovisor.org/technology/xdp
[66]
https://blogs.igalia.com/dpino/2019/01/10/the-express-data-path/:https://blogs.igalia.com/dpino/2019/01/10/the-express-data-path/
[67]
https://pantheon.tech/what-is-af_xdp/:https://pantheon.tech/what-is-af_xdp/
[68]
https://github.com/iovisor/bpf-docs/blob/master/Express_Data_Path.pdf:https://github.com/iovisor/bpf-docs/blob/master/Express_Data_Path.pdf
[69]
https://github.com/xdp-project/xdp-paper/blob/master/xdp-the-express-data-path.pdf:https://github.com/xdp-project/xdp-paper/blob/master/xdp-the-express-data-path.pdf
[70]
http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf:http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf
[71]
https://arthurchiao.art/blog/firewalling-with-bpf-xdp/:https://arthurchiao.art/blog/firewalling-with-bpf-xdp/
[72]
https://archive.fosdem.org/2018/schedule/event/xdp/attachments/slides/2220/export/events/attachments/xdp/slides/2220/fosdem18_SdN_NFV_qmonnet_XDPoffload.pdf:https://archive.fosdem.org/2018/schedule/event/xdp/attachments/slides/2220/export/events/attachments/xdp/slides/2220/fosdem18_SdN_NFV_qmonnet_XDPoffload.pdf
[73]
https://people.netfilter.org/hawk/presentations/KernelRecipes2018/XDP_Kernel_Recipes_2018.pdf:https://people.netfilter.org/hawk/presentations/KernelRecipes2018/XDP_Kernel_Recipes_2018.pdf
[74]
https://legacy.netdevconf.info/2.1/session.html?gospodarek:https://legacy.netdevconf.info/2.1/session.html?gospodarek
[75]
查看:https://ntk148v.github.io/posts/linux-network-performance-ultimate-guide/ebpf/README.md
[76]
Linux 4.18:https://www.kernel.org/doc/html/v4.18/networking/af_xdp.html
[77]
DPDK:https://doc.dpdk.org/guides/nics/af_xdp.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Y4CAZywJbvySrI1ZHx8McQ