網絡帶寬的單位bps,b表示的bit,一個Byte=8bit。一般常說的10M帶寬,10Mbps=10/8=1.25MBps(Byte)
Mps是每秒傳輸的兆比特位數,是速度單位。MBps,指每秒傳輸多少兆字節,Mbps=Mbit/s即兆比特每秒。
千兆網卡以太網理論上的限制是 128MB。這個數字從何而來?看看這些計算:
1Gb = 1024Mb;1024Mb/8 = 128MB;"b" = "bits,"、"B" = "bytes"
千兆帶寬,都是全雙工的,即上行(上傳)與下行(下載)都是千兆,理論都為128MB/s,但是網絡都有損耗,按20%損耗實際帶寬會是100MB/s。
一、環境檢查
linux下網卡相關檢查
1、先用ifconfig看看有多少塊網卡和bonding。bonding是個很棒的東西,可以把多塊網卡綁起來,突破單塊網卡的帶寬限制。
2、檢查每塊網卡的速度,比如"ethtool eth0"。再檢查bonding,比如"cat /proc/net/bonding/bond0", 留意其Bonding Mode是負載均衡的,再留意其捆綁的網卡的速度。
3、查看網卡是否支持多隊列,是否打開了多隊列,最后確認每個隊列是否綁定到不同的CPU。
4、最后檢查測試客戶機與服務機之間的帶寬,先簡單ping或traceroute 一下得到RTT時間,iperf之類的可稍后。
網卡模式
當主機有1個以上的網卡時,Linux會將多個網卡綁定為一個虛擬的bonded網絡接口,對TCP/IP而言只存在一個bonded網卡。多網卡綁定一方面能夠提高網絡吞吐量,另一方面也可以增強網絡高可用。
可以通過cat/proc/net/bonding/bond0查看本機的Bonding模式
一般很少需要開發去設置網卡Bonding模式。Linux支持7種Bonding模式(詳細的看內核):
-
Mode 0(balance-rr) Round-robin策略,這個模式具備負載均衡和容錯能力
-
Mode 1(active-backup) 主備策略,在綁定中只有一個網卡被激活,其他處於備份狀態
-
Mode 2(balance-xor) XOR策略,通過源MAC地址與目的MAC地址做異或操作選擇slave網卡
-
Mode 3 (broadcast) 廣播,在所有的網卡上傳送所有的報文
-
Mode 4 (802.3ad) IEEE 802.3ad動態鏈路聚合。創建共享相同的速率和雙工模式的聚合組
-
Mode 5 (balance-tlb) Adaptive transmit loadbalancing
-
Mode 6 (balance-alb) Adaptive loadbalancing
查看機器網卡速度
windows
打開"網絡和共享中心"->選擇網絡,查看網絡連接狀態。如下圖所示:

11Mbps
linux
使用查詢及設置網卡參數的命令ethtool(http://man.linuxde.net/ethtool)查看,如使用ethtool eth1
# ethtool eth1 Settings for eth1: Supported ports: [ TP ] Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supports auto-negotiation: Yes Advertised link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Advertised auto-negotiation: Yes Speed: 1000Mb/s Duplex: Full Port: Twisted Pair PHYAD: 0 Transceiver: internal Auto-negotiation: on Supports Wake-on: umbg Wake-on: d Link detected: yes
操作完畢后,輸出信息中Speed:這一項就指示了網卡的速度。
網卡多隊列及中斷綁定
隨着網絡的帶寬的不斷提升,單核CPU已經不能滿足網卡的需求,這時通過多隊列網卡驅動的支持,可以將每個隊列通過中斷綁定到不同的CPU核上,充分利用多核提升數據包的處理能力。
1)查看網卡是否支持多隊列
使用lspci -vvv命令,找到Ethernetcontroller項:

如果有MSI-X, Enable+ 並且Count > 1,則該網卡是多隊列網卡。網卡的中斷機制是MSI-X,即網卡的每個隊列都可以分配中斷(MSI-X支持2048個中斷)。
備注:linux 2.6.32 上有lspci命令,但無-vvv參數。
2)查看是否打開了網卡多隊列。
使用命令cat/proc/interrupts,如果看到eth0-TxRx-0表明多隊列支持已經打開:

- /proc/interrupts:該文件存放了每個I/O設備的對應中斷號、每個CPU的中斷數、中斷類型。
- /proc/irq/:該目錄下存放的是以IRQ號命名的目錄,如/proc/irq/40/,表示中斷號為40的相關信息
- /proc/irq/[irq_num]/smp_affinity:該文件存放的是CPU位掩碼(十六進制)。修改該文件中的值可以改變CPU和某中斷的親和性
- /proc/irq/[irq_num]/smp_affinity_list:該文件存放的是CPU列表(十進制)。注意,CPU核心個數用表示編號從0開始,如cpu0,cpu1等
-
smp_affinity_list和smp_affinity任意更改一個文件都會生效,兩個文件相互影響,只不過是表示方法不一致,但一般都是修改smp_affinity 文件
3)最后確認每個隊列是否綁定到不同的CPU。
cat/proc/interrupts查詢到每個隊列的中斷號,對應的文件/proc/irq/${IRQ_NUM}/smp_affinity為中斷號IRQ_NUM綁定的CPU核的情況。以十六進制表示,每一位代表一個CPU核:(00000001)代表CPU0(00000010)代表CPU1(00000011)代表CPU0和CPU1。
中斷的親緣性設置可以在 cat /proc/irq/${中斷號}/smp_affinity 或 cat /proc/irq/${中斷號}/smp_affinity_list 中確認,前者是16進制掩碼形式,后者是以CPU Core序號形式。例如下圖中,將16進制的400轉換成2進制后,為 10000000000,“1”在第10位上,表示親緣性是第10個CPU Core。

那為什么中斷號只設置一個CPU Core呢?而不是為每一個中斷號設置多個CPU Core平行處理。經過測試,發現當給中斷設置了多個CPU Core后,它也僅能由設置的第一個CPU Core來處理,其他的CPU Core並不會參與中斷處理,原因猜想是當CPU可以平行收包時,不同的核收取了同一個queue的數據包,但處理速度不一致,導致提交到IP層后的順序也不一致,這就會產生亂序的問題,由同一個核來處理可以避免了亂序問題。
如果綁定的不均衡,可以手工設置,例如:
echo "1" > /proc/irq/99/smp_affinity echo "2" > /proc/irq/100/smp_affinity echo "4" > /proc/irq/101/smp_affinity echo "8" > /proc/irq/102/smp_affinity echo "10" > /proc/irq/103/smp_affinity echo "20" > /proc/irq/104/smp_affinity echo "40" > /proc/irq/105/smp_affinity echo "80" > /proc/irq/106/smp_affinity
上述命令使用時需要注意一個是權限,一個是不同系統之間命令的差異。
理論參考:多隊列網卡CPU中斷均衡
檢查實際帶寬
可以先做ping、traceroute看下 參考資料:網絡丟包分析
- 監控總體帶寬使用――nload、bmon、slurm、bwm-ng、cbm、speedometer和netload
- 監控總體帶寬使用(批量式輸出)――vnstat、ifstat、dstat和collectl
- 每個套接字連接的帶寬使用――iftop、iptraf、tcptrack、pktstat、netwatch和trafshow
- 每個進程的帶寬使用――nethogs
可以在系統不繁忙或者臨時下線前檢測客戶端和server或者proxy 的帶寬:
1)使用 iperf -s 命令將 Iperf 啟動為 server 模式:
iperf –s
————————————————————
Server listening on TCP port 5001
TCP window size: 8.00 KByte (default)
————————————————————
2)啟動客戶端,向IP為10.230.48.65的主機發出TCP測試,並每2秒返回一次測試結果,以Mbytes/sec為單位顯示測試結果:
iperf -c 10.230.48.65 -f M -i 2
在服務端上,運行:
# iperf -s -f M
這台機器將用作服務器並以'M' = MBytes/sec為單位輸出執行速度。
在客戶端節點上,運行:
# iperf -c ginger -P 4 -f M -w 256k -t 60
兩個屏幕上的結果都指示了速度是多少。在使用千兆網卡的普通服務器上,可能會看到速度約為 112MB。這是 TCP 堆棧和物理電纜中的常用帶寬。通過以端到端的方式連接兩台服務器,每台服務器使用兩個聯結的以太網卡,獲得了約 220MB的帶寬。
ping命令

上邊Ping www.a.shifen.com [220.181.111.188],這里的地址是指域名對應的服務器地址。
如hosts文件里,配置 220.181.111.188 www.a.shifen.com
tracert

查看環形緩沖區個數
它位於NIC和IP層之間,是一個典型的FIFO(先進先出)環形隊列。RingBuffer沒有包含數據本身,而是包含了指向sk_buff(socketkernel buffers)的描述符。
可以使用ethtool-g eth0查看當前RingBuffer的設置:

上面的例子接收隊列為4096,傳輸隊列為256。
ifconfig
ifconfig的輸出中有兩項,分別是:
RX==receive,接收,從開啟到現在接收封包的情況,是下行流量。
TX==Transmit,發送,從開啟到現在發送封包的情況,是上行流量。
ifconfig觀察接收和傳輸隊列的運行狀況:
[root@test etc]# ifconfig eth1
eth1 Link encap:Ethernet HWaddr 00:26:B9:58:19:88 inet addr:192.168.0.46 Bcast:192.168.0.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6049782538 errors:0 dropped:2373 overruns:0 frame:0 TX packets:7415059121 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2611876455455 (2.3 TiB) TX bytes:6028598337794 (5.4 TiB) Interrupt:114 Memory:d8000000-d8012800
RX errors: 表示總的收包的錯誤數量,這包括 too-long-frames 錯誤,Ring Buffer 溢出錯誤,crc 校驗錯誤,幀同步錯誤,fifo overruns 以及 missed pkg 等等。
RX dropped: 表示數據包已經進入了 Ring Buffer,但是由於內存不夠等系統原因,導致在拷貝到內存的過程中被丟棄。
RX overruns: 表示了 fifo 的 overruns,這是由於 Ring Buffer(aka Driver Queue) 傳輸的 IO 大於 kernel 能夠處理的 IO 導致的,而 Ring Buffer 則是指在發起 IRQ 請求之前的那塊 buffer。很明顯,overruns 的增大意味着數據包沒到 Ring Buffer 就被網卡物理層給丟棄了,而 CPU 無法及時的處理中斷是造成 Ring Buffer 滿的原因之一,例如中斷分配的不均勻(都壓在 core0),沒有做 affinity 而造成的丟包。
RX frame: 表示 misaligned 的 frames。
當dropped數量持續增加,建議增大RingBuffer,使用ethtool-G進行設置。
對於 TX 的來說,counter 增大的原因主要包括 aborted transmission, errors due to carrirer, fifo error, heartbeat erros 以及 windown error,而 collisions 則表示由於 CSMA/CD 造成的傳輸中斷。
txqueuelen 1000
QDisc(queueing discipline )位於IP層和網卡的ringbuffer之間。QDisc實現了流量管理的高級功能,包括流量分類,優先級和流量整形(rate-shaping)。可以使用tc命令配置QDisc。QDisc的隊列長度由txqueuelen設置,和接收數據包的隊列長度由內核參數net.core.netdev_max_backlog控制所不同,txqueuelen是和網卡關聯。
RX和TX輸出的值使有的單位參數是bytes,而利用該命令,我們也可以配置腳本進行流量檢測。
大名鼎鼎的nagios就有一個插件check_traffic,通過ifconfig的輸出的RX、TX值通過之間的差,再除去中間間隔的時間算出流量大小的。該插件的下載頁為:https://github.com/cloved/check_traffic/downloads 。
現摘錄其中部分關於度量值轉換的部分如下:
#to K
uIn=`echo "$ctbpsIn / 1024" | bc` uOut=`echo "$ctbpsOut / 1024" | bc` #to M if [ "$isM" = "True" ]; then uIn=`echo "scale=$Scale; $uIn / 1024" | bc` uOut=`echo "scale=$Scale; $uOut / 1024" | bc` fi #to B if [ "$isB" = "True" ]; then uIn=`echo "scale=$Scale; $uIn / 8" | bc` uOut=`echo "scale=$Scale; $uOut / 8" | bc`
二、監控
關於CPU中斷的監控
-
動態監控CPU中斷情況,觀察中斷變化
watch -d -n 1 ‘cat /proc/interrupts’
-
查看網卡中斷相關信息
cat /proc/interrupts | grep -E “eth|CPU”
-
網卡親和性設置
修改proc/irq/irq_number/smp_affinity之前,先停掉irq自動調節服務,不然修改的值就會被覆蓋。
/etc/init.d/irqbalance stop
通過查看網卡中斷相關信息,得到網卡中斷為19
[root@master ~]# cd /proc/irq/19 [root@master 19]# cat smp_affinity 00000000,00000000,00000000,00000001 [root@master 19]# cat smp_affinity_list 0
- 查看某個進程的CPU親和性
# taskset -p 30011 pid 30011's current affinity mask: ff
- 設置某個進程的CPU親和性
# taskset -p 1 30011 pid 30011's current affinity mask: ff pid 30011's new affinity mask: 1
- 使用-c選項可以將一個進程對應到多個CPU上去
# taskset -p -c 1,3 30011 pid 30011's current affinity list: 0 pid 30011's new affinity list: 1,3 # taskset -p -c 1-7 30011 pid 30011's current affinity list: 1,3 pid 30011's new affinity list: 1-7
可執行test.sh查看smp_affinity_list中的值的變化
#!/bin/bash irq=`grep 'eth' /proc/interrupts | awk '{print $1}' | cut -d : -f 1` for i in $irq do num=`cat /proc/irq/$i/smp_affinity_list` echo /proc/irq/$i/smp_affinity_list" "$num done
網卡綁定的時候最好和一個物理CPU的核挨個綁定,這樣避免L1,L2,L3踐踏。
使用systemtap診斷測試環境軟中斷分布的方法
global hard, soft, wq probe irq_handler.entry { hard[irq, dev_name]++; } probe timer.s(1) { println("==irq number:dev_name") foreach( [irq, dev_name] in hard- limit 5) { printf("%d,%s->%d\n", irq, kernel_string(dev_name), hard[irq, dev_name]); } println("==softirq cpu:h:vec:action") foreach( [c,h,vec,action] in soft- limit 5) { printf("%d:%x:%x:%s->%d\n", c, h, vec, symdata(action), soft[c,h,vec,action]); } println("==workqueue wq_thread:work_func") foreach( [wq_thread,work_func] in wq- limit 5) { printf("%x:%x->%d\n", wq_thread, work_func, wq[wq_thread, work_func]); } println("\n") delete hard delete soft delete wq } probe softirq.entry { soft[cpu(), h,vec,action]++; } probe workqueue.execute { wq[wq_thread, work_func]++ } probe begin { println("~") }
執行結果:
==irq number:dev_name 87,eth0-0->1693 90,eth0-3->1263 95,eth1-3->746 92,eth1-0->703 89,eth0-2->654 ==softirq cpu:h:vec:action 0:ffffffff81a83098:ffffffff81a83080:0xffffffff81461a00->8928 0:ffffffff81a83088:ffffffff81a83080:0xffffffff81084940->626 0:ffffffff81a830c8:ffffffff81a83080:0xffffffff810ecd70->614 16:ffffffff81a83088:ffffffff81a83080:0xffffffff81084940->225 16:ffffffff81a830c8:ffffffff81a83080:0xffffffff810ecd70->224 ==workqueue wq_thread:work_func ffff88083062aae0:ffffffffa01c53d0->10 ffff88083062aae0:ffffffffa01ca8f0->10 ffff88083420a080:ffffffff81142160->2 ffff8808343fe040:ffffffff8127c9d0->2 ffff880834282ae0:ffffffff8133bd20->1
下面是action對應的符號信息:
addr2line -e /usr/lib/debug/lib/modules/2.6.32-431.20.3.el6.mt20161028.x86_64/vmlinux ffffffff81461a00 /usr/src/debug/kernel-2.6.32-431.20.3.el6/linux-2.6.32-431.20.3.el6.mt20161028.x86_64/net/core/dev.c:4013
打開這個文件,發現它是在執行 static void net_rx_action(struct softirq_action *h)這個函數,而這個函數正是NET_RX_SOFTIRQ 對應的軟中斷處理程序。因此可以確認網卡的軟中斷在機器上分布非常不均,而且主要集中在CPU 0上。通過/proc/interrupts能確認硬中斷集中在CPU 0上,因此軟中斷也都由CPU 0處理,如何優化網卡的中斷成為了我們關注的重點。
set_irq_affinity.sh
# cat set_irq_affinity.sh # setting up irq affinity according to /proc/interrupts # 2008-11-25 Robert Olsson # 2009-02-19 updated by Jesse Brandeburg # # > Dave Miller: # (To get consistent naming in /proc/interrups) # I would suggest that people use something like: # char buf[IFNAMSIZ+6]; # # sprintf(buf, "%s-%s-%d", # netdev->name, # (RX_INTERRUPT ? "rx" : "tx"), # queue->index); # # Assuming a device with two RX and TX queues. # This script will assign: # # eth0-rx-0 CPU0 # eth0-rx-1 CPU1 # eth0-tx-0 CPU0 # eth0-tx-1 CPU1 # set_affinity() { if [ $VEC -ge 32 ] then MASK_FILL="" MASK_ZERO="00000000" let "IDX = $VEC / 32" for ((i=1; i<=$IDX;i++)) do MASK_FILL="${MASK_FILL},${MASK_ZERO}" done let "VEC -= 32 * $IDX" MASK_TMP=$((1<<$VEC)) MASK=`printf "%X%s" $MASK_TMP $MASK_FILL` else MASK_TMP=$((1<<(`expr $VEC + $CORE`))) MASK=`printf "%X" $MASK_TMP` fi printf "%s mask=%s for /proc/irq/%d/smp_affinity\n" $DEV $MASK $IRQ printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity } if [ $# -ne 2 ] ; then echo "Description:" echo " This script attempts to bind each queue of a multi-queue NIC" echo " to the same numbered core, ie tx0|rx0 --> cpu0, tx1|rx1 --> cpu1" echo "usage:" echo " $0 core eth0 [eth1 eth2 eth3]" exit fi CORE=$1 # check for irqbalance running IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?` if [ "$IRQBALANCE_ON" == "0" ] ; then echo " WARNING: irqbalance is running and will" echo " likely override this script's affinitization." echo " Please stop the irqbalance service and/or execute" echo " 'killall irqbalance'" fi # # Set up the desired devices. # shift 1 for DEV in $* do for DIR in rx tx TxRx do MAX=`grep $DEV-$DIR /proc/interrupts | wc -l` if [ "$MAX" == "0" ] ; then MAX=`egrep -i "$DEV:.*$DIR" /proc/interrupts | wc -l` fi if [ "$MAX" == "0" ] ; then echo no $DIR vectors found on $DEV continue fi for VEC in `seq 0 1 $MAX` do IRQ=`cat /proc/interrupts | grep -i $DEV-$DIR-$VEC"$" | cut -d: -f1 | sed "s/ //g"` if [ -n "$IRQ" ]; then set_affinity else IRQ=`cat /proc/interrupts | egrep -i $DEV:v$VEC-$DIR"$" | cut -d: -f1 | sed "s/ //g"` if [ -n "$IRQ" ]; then set_affinity fi fi done done done
腳本參數是:set_irq_affinity.sh core eth0 [eth1 eth2 eth3] 可以一次設置多個網卡,core的意思是從這個號開始遞增。
#./set_irq_affinity.sh 0 em1 no rx vectors found on em1 no tx vectors found on em1 em1 mask=1 for /proc/irq/109/smp_affinity em1 mask=2 for /proc/irq/110/smp_affinity em1 mask=4 for /proc/irq/111/smp_affinity em1 mask=8 for /proc/irq/112/smp_affinity em1 mask=10 for /proc/irq/113/smp_affinity em1 mask=20 for /proc/irq/114/smp_affinity em1 mask=40 for /proc/irq/115/smp_affinity em1 mask=80 for /proc/irq/116/smp_affinity #./set_irq_affinity.sh 8 em2 no rx vectors found on em2 no tx vectors found on em2 em2 mask=100 for /proc/irq/118/smp_affinity em2 mask=200 for /proc/irq/119/smp_affinity em2 mask=400 for /proc/irq/120/smp_affinity em2 mask=800 for /proc/irq/121/smp_affinity em2 mask=1000 for /proc/irq/122/smp_affinity em2 mask=2000 for /proc/irq/123/smp_affinity em2 mask=4000 for /proc/irq/124/smp_affinity em2 mask=8000 for /proc/irq/125/smp_affinity
關於網絡流量的監控
watch -n 1 "/sbin/ifconfig eth0|grep bytes"
出現丟包問題分析思路
如果網絡出現丟包,發現丟包是因為隊列中的數據包超過了 netdev_max_backlog 造成了丟棄,因此首先想到是臨時調大netdev_max_backlog能否解決燃眉之急,事實證明,對於輕微丟包調大參數可以緩解丟包,但對於大量丟包則幾乎不怎么管用,內核處理速度跟不上收包速度的問題還是客觀存在,本質還是因為單核處理中斷有瓶頸,即使不丟包,服務響應速度也會變慢。因此如果能同時使用多個CPU Core來處理中斷,就能顯著提高中斷處理的效率,並且每個CPU都會實例化一個softnet_data對象,隊列數也增加了。
這部分丟包可以在cat /proc/net/softnet_stat的輸出結果中進行確認:
參考資料:
