一.网卡初始化
网络子系统初始化
为每个CPU都初始化如下一个结构:
struct softnet_data {
struct Qdisc *output_queue;
struct Qdisc **output_queue_tailp;
struct list_head poll_list; // 是个双向链表,存储的是注册的中断处理方法信息
struct sk_buff *completion_queue;
struct sk_buff_head process_queue;
/* stats */
unsigned int processed;
unsigned int time_squeeze;
unsigned int cpu_collision;
unsigned int received_rps;
....
}
网卡接受数据之前,内核创建好ksoftirqd内核线程(有几个cpu就创建几个),注册好各个协议的处理函数,网卡的子系统提前初始化并启动。
1. ksoftirqd功能:创建出来后,进入自己的线程循环,持续判断是否有软中断需要处理.
2. 每个cpu都会初始化一个softnet_data结构,并且注册一个接受队列跟传输队列,两个注册处理函数.
3. 在net_dev_init函数中,为每一种中断都注册一个处理函数. 内核存储的变量softirq_vec[nr].action = action,并且内部针对协议栈的的处理,也是提前注册好对应的处理函数。例如inet_init网络初始化,通过讲相关的协议,如tcp/udp,注册到inet_protos数组中, ip传输层,注册一个ptype_base的哈希表注册一个ip_rcv函数。后续有数据包触发软中断,根据协议类型调用不同的中断函数进行处理.
4.网卡驱动初始化,通过module_init向内核注册一个初始化函数,当驱动程序被加载,内核回调用该函数.当网卡启动的时候,内核调用初始化函数(之前提供的函数),分配ringbuff,注册中断处理函数,打开硬中断,等待接收包。
5.当网卡接收到数据,先通过DMA讲讲数据拷贝到内存中,之后向cpu发出一个硬中断,cpu接收硬中断后,调用中断处理函数,该函数是实质上抛出一个软中断给内核线程ksoftirqd,内核线程收到软中断后,并将对应的数据发送到协议栈,最后向上推送到用户进程处理。「 那是个cpu处理的硬中断,最终就由那个cpu处理后续的软中断,所以有时候看到某个cpu负荷非常大,但是其他的cpu却负荷很小 」
二.网卡信息查看&性能几个命令
列举RingBuff的长度跟大小
ethtool -g 网卡名
[root@localhost]# ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX: 1024
RX Mini: 0
RX Jumbo: 0
TX: 1024
Current hardware settings:
RX: 1024
RX Mini: 0
RX Jumbo: 0
TX: 1024
统计网卡就接收数据包情况:
ethtool -S 网卡名
查看两个关键字段:
以下两个字段可能为旧系统的展示字段
rx_fifo_errors:0
tx_fifo_errors:0
//centos8查询的下面新字段表示:
[root@localhost ~]# ethtool -S ens160
NIC statistics:
Tx Queue#: 0
TSO pkts tx: 2
TSO bytes tx: 3028
ucast pkts tx: 948
ucast bytes tx: 88676
mcast pkts tx: 3
mcast bytes tx: 282
bcast pkts tx: 0
bcast bytes tx: 0
pkts tx err: 0 // TX
pkts tx discard: 0
drv dropped tx total: 0
too many frags: 0
giant hdr: 0
hdr err: 0
tso: 0
ring full: 0
pkts linearized: 0
hdr cloned: 0
giant hdr: 0
Rx Queue#: 0
LRO pkts rx: 0
LRO byte rx: 0
ucast pkts rx: 1291
ucast bytes rx: 105972
mcast pkts rx: 32
mcast bytes rx: 3584
bcast pkts rx: 367
bcast bytes rx: 23854
pkts rx OOB: 0
pkts rx err: 0 // RX
drv dropped rx total: 0
err: 0
fcs: 0
rx buf alloc fail: 0
tx timeout count: 0
pkts tx err: 0
pkts rx err: 0
如果上centos8系统查询的字段,如果不为0,则表示数据包因RingBuff装不下而丢包.此时就需要调整队列大小。
修改网卡的RingBuff队列信息
ethtool -G 网卡名 rx 1024 tx 1024
[root@localhost ~]# ethtool -g ens160
Ring parameters for ens160:
Pre-set maximums:
RX: 4096
RX Mini: n/a
RX Jumbo: 4096
TX: 4096
Current hardware settings:
RX: 2048
RX Mini: n/a
RX Jumbo: 512
TX: 1024
[root@localhost ~]# ethtool -G ens160 rx 1024 tx 1024
[root@localhost ~]# ethtool -g ens160
Ring parameters for ens160:
Pre-set maximums:
RX: 4096
RX Mini: n/a
RX Jumbo: 4096
TX: 4096
Current hardware settings:
RX: 1024
RX Mini: n/a
RX Jumbo: 512
TX: 2048
(可以通过增大RingBuff避免丢包,但是该方法有个缺陷,队列太大,会造成数据包在RingBuff中等待耗时,从而也会影响性能)
提高数据包的处理速度:
ethtool -l 网卡名
查看网卡的队列信息,包含支持总数量 + 当前开启的数量
Combined: 最大数
Current ... :当前开启数量
[root@locahost]: ethtool -l ens160
Channel parameters for ens160:
Pre-set maximums:
RX: 0
TX: 0
Other: 0
Combined: 4
Current hardware settings:
RX: 0
TX: 0
Other: 0
Combined: 1
查看网卡当前生效的队列数:
ls /sys/class/net/网卡名/queue
[root@localhost ~]# ls /sys/class/net/ens160/queues
rx-0 tx-0
修改生效的队列数 (增大队列可以提升网络数据包性能)
ehttool -L 网卡名 combined 队列数
[root@localhost]# ethtool -L ens160 combined 2
[root@localhost]# ethtool -l ens160
Channel parameters for ens160:
Pre-set maximums:
RX: 0
TX: 0
Other: 0
Combined: 4
Current hardware settings:
RX: 0
TX: 0
Other: 0
Combined: 2
查看修改后的队列数:
增加队列数为2
[root@localhost]# ls /sys/class/net/ens160/queues/
rx-0 rx-1 tx-0 tx-1
查看队列对应的硬中断号
cat /proc/interrupts
因信息太多,致列出队列应对的中断号信息,虚拟机只有一个:
[root@localhost ~]# cat /proc/interrupts
CPU0
56: 238605 PCI-MSI 1572864-edge ens160-rxtx-0
查看当前队列亲和的是那个cpu,一个整数的二进制的位数标记亲和的那个cpu,例如smp_affinity = 8,对应二进制1000,第四位为1,则表示亲和的是第四个cpu,也就是cpu3(编号从0开始)
cat /proc/irq/中断号/smp_affinity ,虚拟机只有一个核,亲和cpu0,第一个cpu。所以如下二进制位第一位为1.
[root@localhost ~]#
[root@localhost ~]# cat /proc/irq/56/smp_affinity
00000001
通过修改中断亲和性到指定的cpu,可以分散的打乱某个cpu高负荷.
echo "4" > /proc/irq/53/smp_affinity
(每个队列都有独立的,不同的中断号,所以不同的队列再接收数据后,将数据提取到自己的RingBuff中,分别向不通的cpu发起硬中断,最终有不同的cpu处理后续软中断,最后由内核线程处理收包处理),这也就是为什么增大网卡队列数可以提升数据包处理性能的原因所在;
RingBuff概念;
总的来说是一块特殊的区域,通常说成是一个环形队列。实质上是由igb_rx_buffer和e1000_adv_rx_desc两个环形队列跟若干skb内存块组成的。其中两个队列的大小是预先分配好的。而skb是根据收包过程动态分配的。收包时回讲skb从队列中取走,并重新分配一个skb挂上。具体的大小信息可以通过ethtool工具查看.