最近工作中遇到某個服務器應用程序 UDP 丟包,在排查過程中查閱了很多資料,總結出來這篇文章,供更多人參考。
在開始之前,我們先用一張圖解釋 linux 系統接收網絡報文的過程。
- 首先網絡報文通過物理網線發送到網卡
- 網絡驅動程序會把網絡中的報文讀出來放到 ring buffer 中,這個過程使用 DMA(Direct Memory Access),不需要 CPU 參與
- 內核從 ring buffer 中讀取報文進行處理,執行 IP 和 TCP/UDP 層的邏輯,最后把報文放到應用程序的 socket buffer 中
- 應用程序從 socket buffer 中讀取報文進行處理
在接收 UDP 報文的過程中,圖中任何一個過程都可能會主動或者被動地把報文丟棄,因此丟包可能發生在網卡和驅動,也可能發生在系統和應用。
之所以沒有分析發送數據流程,一是因為發送流程和接收類似,只是方向相反;另外發送流程報文丟失的概率比接收小,只有在應用程序發送的報文速率大于內核和網卡處理速率時才會發生。
本篇文章假定機器只有一個名字為 eth0
的 interface,如果有多個 interface 或者 interface 的名字不是 eth0,請按照實際情況進行分析。
NOTE:文中出現的 RX
(receive) 表示接收報文,TX
(transmit) 表示發送報文。
確認有 UDP 丟包發生
要查看網卡是否有丟包,可以使用 ethtool -S eth0
查看,在輸出中查找 bad
或者 drop
對應的字段是否有數據,在正常情況下,這些字段對應的數字應該都是 0。如果看到對應的數字在不斷增長,就說明網卡有丟包。
另外一個查看網卡丟包數據的命令是 ifconfig
,它的輸出中會有 RX
(receive 接收報文)和 TX
(transmit 發送報文)的統計數據:
ifconfig eth0
...
RX packets 3553389376 bytes 2599862532475 (2.3 TiB)
RX errors 0 dropped 1353 overruns 0 frame 0
TX packets 3479495131 bytes 3205366800850 (2.9 TiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...
此外,linux 系統也提供了各個網絡協議的丟包信息,可以使用 netstat -s
命令查看,加上 --udp
可以只看 UDP 相關的報文數據:
GOD]# netstat -s -u
IcmpMsg:
InType0: 3
InType3: 1719356
InType8: 13
InType11: 59
OutType0: 13
OutType3: 1737641
OutType8: 10
OutType11: 263
Udp:
517488890 packets received
2487375 packets to unknown port received.
47533568 packet receive errors
147264581 packets sent
12851135 receive buffer errors
0 send buffer errors
UdpLite:
IpExt:
OutMcastPkts: 696
InBcastPkts: 2373968
InOctets: 4954097451540
OutOctets: 5538322535160
OutMcastOctets: 79632
InBcastOctets: 934783053
InNoECTPkts: 5584838675
對于上面的輸出,關注下面的信息來查看 UDP 丟包的情況:
packet receive errors
不為空,并且在一直增長說明系統有 UDP 丟包packets to unknown port received
表示系統接收到的 UDP 報文所在的目標端口沒有應用在監聽,一般是服務沒有啟動導致的,并不會造成嚴重的問題receive buffer errors
表示因為 UDP 的接收緩存太小導致丟包的數量
NOTE:并不是丟包數量不為零就有問題,對于 UDP 來說,如果有少量的丟包很可能是預期的行為,比如丟包率(丟包數量/接收報文數量)在萬分之一甚至更低。
網卡或者驅動丟包
之前講過,如果 ethtool -S eth0
中有 rx_***_errors
那么很可能是網卡有問題,導致系統丟包,需要聯系服務器或者網卡供應商進行處理。
# ethtool -S eth0 | grep rx_ | grep errors
rx_crc_errors: 0
rx_missed_errors: 0
rx_long_length_errors: 0
rx_short_length_errors: 0
rx_align_errors: 0
rx_errors: 0
rx_length_errors: 0
rx_over_errors: 0
rx_frame_errors: 0
rx_fifo_errors: 0
netstat -i
也會提供每個網卡的接發報文以及丟包的情況,正常情況下輸出中 error 或者 drop 應該為 0。
如果硬件或者驅動沒有問題,一般網卡丟包是因為設置的緩存區(ring buffer)太小,可以使用 ethtool
命令查看和設置網卡的 ring buffer。
ethtool -g
可以查看某個網卡的 ring buffer,比如下面的例子
# ethtool -g eth0
Ring parameters for eth0:
maximums:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Current hardware settings:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256
Pre-set 表示網卡最大的 ring buffer 值,可以使用 ethtool -G eth0 rx 8192
設置它的值。
Linux 系統丟包
linux 系統丟包的原因很多,常見的有:UDP 報文錯誤、防火墻、UDP buffer size 不足、系統負載過高等,這里對這些丟包原因進行分析。
UDP 報文錯誤
如果在傳輸過程中UDP 報文被修改,會導致 checksum 錯誤,或者長度錯誤,linux 在接收到 UDP 報文時會對此進行校驗,一旦發明錯誤會把報文丟棄。
如果希望 UDP 報文 checksum 及時有錯也要發送給應用程序,可以在通過 socket 參數禁用 UDP checksum 檢查:
int disable = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_NO_CHECK, (void*)&disable, sizeof(disable)
原文:https://cizixs.com/2018/01/13/linux-udp-packet-drop-debug/
-
cpu
+關注
關注
68文章
10854瀏覽量
211587 -
Linux
+關注
關注
87文章
11292瀏覽量
209333 -
dma
+關注
關注
3文章
560瀏覽量
100549 -
網絡驅動
+關注
關注
0文章
7瀏覽量
7409
發布評論請先 登錄
相關推薦
評論