Linux 網(wǎng)卡收包流程如下
網(wǎng)卡收到數(shù)據(jù)包
將數(shù)據(jù)包從網(wǎng)卡硬件緩存移動到服務(wù)器內(nèi)存中(DMA方式,不經(jīng)過CPU)
通過硬中斷通知CPU處理
CPU通過軟中斷通知內(nèi)核處理
經(jīng)過TCP/IP協(xié)議棧處理
應(yīng)用程序通過read()從socket buffer讀取數(shù)據(jù)
網(wǎng)卡丟包
我們先看下ifconfig的輸出:
# ifconfig eth0 eth0: flags=4163mtu 1500 inet 10.5.224.27 netmask 255.255.255.0 broadcast 10.5.224.255 inet6 fe80::5054fea4:44ae prefixlen 64 scopeid 0x20 ether 52:54:00:a4:44:ae txqueuelen 1000 (Ethernet) RX packets 9525661556 bytes 10963926751740 (9.9 TiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8801210220 bytes 12331600148587 (11.2 TiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0Copy
登錄系統(tǒng)底層,檢查管理網(wǎng)網(wǎng)卡收發(fā)是否存在持續(xù)增長的errors包,使用命令:
# watch -n 1 "ifconfig eth0" # watch -n 1 ip -s link show eth0 說明:watch可以將命令的輸出結(jié)果輸出到標(biāo)準(zhǔn)輸出設(shè)備,多用于周期性執(zhí)行命令/定時(shí)執(zhí)行命令,-n或-interval來指定間隔的時(shí)間,缺省每2秒運(yùn)行一下程序;eth1為網(wǎng)卡名稱。
正常的
異常的
RX(receive) 代表接收報(bào)文, TX(transmit) 表示發(fā)送報(bào)文。
RX errors: 表示總的收包的錯(cuò)誤數(shù)量,這包括 too-long-frames 錯(cuò)誤,Ring Buffer 溢出錯(cuò)誤,crc 校驗(yàn)錯(cuò)誤,幀同步錯(cuò)誤,fifo overruns 以及 missed pkg 等等。
RX dropped: 表示數(shù)據(jù)包已經(jīng)進(jìn)入了 Ring Buffer,但是由于內(nèi)存不夠等系統(tǒng)原因,導(dǎo)致在拷貝到內(nèi)存的過程中被丟棄。
RX overruns: 表示 fifo 的 overruns,由于 Ring Buffer(aka Driver Queue) 傳輸?shù)?IO 大于 kernel 能夠處理的 IO 導(dǎo)致的,而 Ring Buffer 則是指在發(fā)起 IRQ 請求之前的那塊 buffer。很明顯,overruns 的增大意味著數(shù)據(jù)包沒到 Ring Buffer 就被網(wǎng)卡物理層給丟棄了,CPU 無法及時(shí)的處理中斷是造成 Ring Buffer 滿的原因之一,可能原因是因?yàn)?interruprs 分布的不均勻,沒有做 affinity 而造成的丟包。
RX frame: 表示 misaligned 的 frames。
dropped 與 overruns 的區(qū)別:
dropped,表示這個(gè)數(shù)據(jù)包已經(jīng)進(jìn)入到網(wǎng)卡的接收緩存 fifo 隊(duì)列,并且開始被系統(tǒng)中斷處理準(zhǔn)備進(jìn)行數(shù)據(jù)包拷貝(從網(wǎng)卡緩存 fifo 隊(duì)列拷貝到系統(tǒng)內(nèi)存),但由于此時(shí)的系統(tǒng)原因(比如內(nèi)存不夠等)導(dǎo)致這個(gè)數(shù)據(jù)包被丟掉,即這個(gè)數(shù)據(jù)包被 Linux 系統(tǒng)丟掉。
overruns,表示這個(gè)數(shù)據(jù)包還沒有被進(jìn)入到網(wǎng)卡的接收緩存 fifo 隊(duì)列就被丟掉,因此此時(shí)網(wǎng)卡的 fifo 是滿的。為什么 fifo 會是滿的?因?yàn)橄到y(tǒng)繁忙,來不及響應(yīng)網(wǎng)卡中斷,導(dǎo)致網(wǎng)卡里的數(shù)據(jù)包沒有及時(shí)的拷貝到系統(tǒng)內(nèi)存, fifo 是滿的就導(dǎo)致后面的數(shù)據(jù)包進(jìn)不來,即這個(gè)數(shù)據(jù)包被網(wǎng)卡硬件丟掉。所以,如果遇到 overruns 非0,需要檢測cpu負(fù)載與cpu中斷情況。
環(huán)形隊(duì)列Ring Buffer溢出
當(dāng)網(wǎng)卡的緩存區(qū)(ring buffer)設(shè)置的太小。網(wǎng)絡(luò)數(shù)據(jù)包到達(dá)(生產(chǎn))的速率快于內(nèi)核處理(消費(fèi))的速率時(shí), Ring Buffer 很快會被填滿,新來的數(shù)據(jù)包將被丟棄。
通過 ethtool 或 /proc/net/dev 可以查看因Ring Buffer滿而丟棄的包統(tǒng)計(jì)
[root@xxx ~]# ethtool -S ens2 | grep fifo rx_fifo_errors: 0 tx_fifo_errors: 0 [root@xxx ~]# cat /proc/net/dev | grep ens2 ens2: 659229 8107 0 0 0 0 0 0 249827 2833 0 0 0 0 0 0
可以通過ethtool 設(shè)置ring buffer 的緩沖區(qū)大小
# 修改網(wǎng)卡eth0接收與發(fā)送硬件緩存區(qū)大小 $ ethtool -G eth0 rx 4096 tx 4096 Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096
中斷過程中的問題
什么是中斷
中斷有兩種:一種硬中斷;一種軟中斷。硬中斷是由硬件產(chǎn)生的,比如,像磁盤,網(wǎng)卡,鍵盤;軟中斷是由當(dāng)前正在運(yùn)行的進(jìn)程所產(chǎn)生的。
硬中斷,是一種由硬件產(chǎn)生的電信號直接發(fā)送到中斷控制器上,然后由中斷控制器向 CPU 發(fā)送信號,CPU 檢測到該信號后,會中斷當(dāng)前的工作轉(zhuǎn)而去處理中斷。然后,處理器會通知內(nèi)核已經(jīng)產(chǎn)生中斷,這樣內(nèi)核就會對這個(gè)中斷進(jìn)行適當(dāng)?shù)奶幚怼?/p>
當(dāng)網(wǎng)卡收到數(shù)據(jù)包時(shí)會產(chǎn)生中斷請求(硬中斷)通知到 CPU,CPU 會中斷當(dāng)前正在運(yùn)行的任務(wù),然后通知內(nèi)核有新數(shù)據(jù)包,內(nèi)核調(diào)用中斷處理程序(軟中斷)進(jìn)行響應(yīng),把數(shù)據(jù)包從網(wǎng)卡緩存及時(shí)拷貝到內(nèi)存,否則會因?yàn)榫彺嬉绯霰粊G棄。剩下的處理和操作數(shù)據(jù)包的工作就會交給軟中斷。
什么是多隊(duì)列網(wǎng)卡
當(dāng)網(wǎng)卡不斷的接收數(shù)據(jù)包,就會產(chǎn)生很多中斷,一個(gè)中斷請求只能被一個(gè)CPU處理, 而現(xiàn)在的機(jī)器都是用多個(gè)CPU,同時(shí)只有一個(gè) CPU 去處理 Ring Buffer 數(shù)據(jù)會很低效,這個(gè)時(shí)候就產(chǎn)生了叫做 Receive Side Scaling(RSS) 或者叫做 multiqueue 的機(jī)制來處理這個(gè)問題, 這就是為啥需要多隊(duì)列的原因。
RSS(Receive Side Scaling)是網(wǎng)卡的硬件特性,實(shí)現(xiàn)了多隊(duì)列。通過多隊(duì)列網(wǎng)卡驅(qū)動加載,獲取網(wǎng)卡型號,得到網(wǎng)卡的硬件 queue 的數(shù)量,并結(jié)合 CPU 核的數(shù)量,最終通過 Sum=Min(網(wǎng)卡 queue,CPU core)得出所要激活的網(wǎng)卡 queue 數(shù)量。
NIC 收到 Frame 的時(shí)候能通過 Hash Function 來決定 Frame 該放在哪個(gè) Ring Buffer 上,觸發(fā)的 IRQ 也可以通過操作系統(tǒng)或者手動配置 IRQ affinity 將 IRQ 分配到多個(gè) CPU 上。這樣 IRQ 能被不同的 CPU 處理,從而做到 Ring Buffer 上的數(shù)據(jù)也能被不同的 CPU 處理,從而提高數(shù)據(jù)的并行處理能力。
RSS 除了會影響到 NIC 將 IRQ 發(fā)到哪個(gè) CPU 之外,不會影響別的邏輯。
什么是RPS
Receive Packet Steering(RPS) 是在 NIC 不支持 RSS 時(shí)候在軟件中實(shí)現(xiàn) RSS 類似功能的機(jī)制。其好處就是對 NIC 沒有要求,任何 NIC 都能支持 RPS,但缺點(diǎn)是 NIC 收到數(shù)據(jù)后 DMA 將數(shù)據(jù)存入的還是一個(gè) Ring Buffer,NIC 觸發(fā) IRQ 還是發(fā)到一個(gè) CPU,還是由這一個(gè) CPU 調(diào)用 driver 的 poll 來將 Ring Buffer 的數(shù)據(jù)取出來。RPS 是在單個(gè) CPU 將數(shù)據(jù)從 Ring Buffer 取出來之后才開始起作用,它會為每個(gè) Packet 計(jì)算 Hash 之后將 Packet 發(fā)到對應(yīng) CPU 的 backlog 中,并通過 Inter-processor Interrupt(IPI) 告知目標(biāo) CPU 來處理 backlog。后續(xù) Packet 的處理流程就由這個(gè)目標(biāo) CPU 來完成。從而實(shí)現(xiàn)將負(fù)載分到多個(gè) CPU 的目的。通常如果開啟了RPS會加重所有 CPU 的負(fù)擔(dān).
IRQ 中斷請求 親和綁定
/proc/interrupts 文件中可以看到各個(gè) CPU 上的中斷情況。
/proc/irq/[irq_num]/smp_affinity_list 可以查看指定中斷當(dāng)前綁定的 CPU。
可以通過配置 IRQ affinity 指定 IRQ 由哪個(gè) CPU 來處理中斷, 先通過 /proc/interrupts 找到 IRQ 號之后,將希望綁定的 CPU 號寫入 /proc/irq/IRQ_NUMBER/smp_affinity,寫入的是 16 進(jìn)制的 bit mask。比如看到隊(duì)列 rx_0 對應(yīng)的中斷號是 41 那就執(zhí)行:
echo 6 > /proc/irq/41/smp_affinity 6 表示的是 CPU2 和 CPU1
0 號 CPU 的掩碼是 0x1 (0001),1 號 CPU 掩碼是 0x2 (0010),2 號 CPU 掩碼是 0x4 (0100),3 號 CPU 掩碼是 0x8 (1000) 依此類推。
softirq 數(shù)統(tǒng)計(jì)
通過 /proc/softirqs 能看到每個(gè) CPU 上 softirq 數(shù)量統(tǒng)計(jì):
cat /proc/softirqs CPU0 CPU1 HI: 1 0 TIMER: 1650579324 3521734270 NET_TX: 10282064 10655064 NET_RX: 3618725935 2446 BLOCK: 0 0 BLOCK_IOPOLL: 0 0 TASKLET: 47013 41496 SCHED: 1706483540 1003457088 HRTIMER: 1698047 11604871 RCU: 4218377992 3049934909
NET_RX 表示網(wǎng)卡收到包時(shí)候觸發(fā)的 softirq,一般看這個(gè)統(tǒng)計(jì)是為了看看 softirq 在每個(gè) CPU 上分布是否均勻,不均勻的話可能就需要做一些調(diào)整。比如上面看到 CPU0 和 CPU1 兩個(gè)差距很大,原因是這個(gè)機(jī)器的 NIC 不支持 RSS,沒有多個(gè) Ring Buffer。開啟 RPS 后就均勻多了。
如何開啟RPS
RPS 默認(rèn)是關(guān)閉的,當(dāng)機(jī)器有多個(gè) CPU 并且通過 softirqs 的統(tǒng)計(jì) /proc/softirqs 發(fā)現(xiàn) NET_RX 在 CPU 上分布不均勻或者發(fā)現(xiàn)網(wǎng)卡不支持 mutiqueue 時(shí),就可以考慮開啟 RPS。
開啟 RPS 需要調(diào)整 /sys/class/net/DEVICE_NAME/queues/QUEUE/rps_cpus 的值。比如執(zhí)行:
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
表示的含義是處理網(wǎng)卡 eth0 的 rx-0 隊(duì)列的 CPU 數(shù)設(shè)置為 f 。即設(shè)置有 15 個(gè) CPU 來處理 rx-0 這個(gè)隊(duì)列的數(shù)據(jù),如果你的 CPU 數(shù)沒有這么多就會默認(rèn)使用所有 CPU 。
netdev_max_backlog調(diào)優(yōu)
netdev_max_backlog 是內(nèi)核從 NIC 收到包后,交由協(xié)議棧(如 IP、TCP )處理之前的緩沖隊(duì)列, 通過softnet_stat可以確定是否發(fā)生了netdev backlog隊(duì)列溢出
[root@xxx ~]# cat /proc/net/softnet_stat 000000bf 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000028 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000c7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000031 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000021d8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000929 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
C#復(fù)制全屏
每一行代表每個(gè) CPU 核的狀態(tài)統(tǒng)計(jì),從 CPU0 依次往下。 每一列代表一個(gè) CPU 核的各項(xiàng)統(tǒng)計(jì):第一列代表中斷處理程序收到的包總數(shù);第二列即代表由于 netdev_max_backlog 隊(duì)列溢出而被丟棄的包總數(shù)。 第3列表示軟中斷一次取走netdev_budget個(gè)數(shù)據(jù)包,或取數(shù)據(jù)包時(shí)間超過2ms的次數(shù)。 第4~8列固定為0,沒有意義。 第9列表示發(fā)送數(shù)據(jù)包時(shí),對應(yīng)的隊(duì)列被鎖住的次數(shù)。
netdev_max_backlog 的默認(rèn)值是 1000,我們可以修改內(nèi)核參數(shù)來調(diào)優(yōu):
sysctl -w net.core.netdev_max_backlog=2000
鏈接:https://www.cnblogs.com/OpenSourceSite/p/18121680
-
Linux
+關(guān)注
關(guān)注
87文章
11345瀏覽量
210395 -
網(wǎng)卡
+關(guān)注
關(guān)注
4文章
314瀏覽量
27458
原文標(biāo)題:Linux之網(wǎng)絡(luò)排錯(cuò)
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論