[置顶] 泰晓 RISC-V 实验箱,配套 30+ 讲嵌入式 Linux 系统开发公开课
初识 Linux 网络栈及常用优化方法
By 法海 of [TinyLab.org][1] Aug 20, 2020
文章简介
基于 ping 流程窥探 Linux 网络子系统,同时介绍各个模块的优化方法。
ping 基本原理
- Client 端发送 ICMP ECHO_REQUEST 报文给 Server
- Server 端回应 ICMP ECHO_REPLY 报文给 Client
这其中涉及基本的二三层转发原理,比如:直接路由、间接路由、ARP 等概念。
这部分不是本文重点,最基本的网络通信原理可以参考这篇文章:
ping 报文发送流程
系统调用层
- sendmsg 系统调用
- 根据目标地址获取路由信息,决定报文出口
ip_append_data()
函数构建 skb(这时才将报文从用户态拷贝到内核态),将报文放入 socket buffer- 调用
ip_push_pending_frames()
,进入 IP 层处理
IP 层
- 填充 IP 头
- 根据 neighbour 信息,填充 MAC 头,调用
dev_queue_xmit()
进入网络设备层
网络设备层
- 选择发送队列
__dev_xmit_skb()
尝试直接发送报文,如果网卡繁忙就将报文放入目标发送队列的 qdisc 队列,并触发 NET_TX_SOFTIRQ 软中断,在后续软中断处理中发送 qdisc 队列中的报文- 驱动层发包函数,将 skb 指向的报文地址填入 tx descriptor,网卡发送报文
- 发送完成后触发中断,回收 tx descriptor(实际实现中一般放在驱动 poll 函数中)
优化点
socket buffer
应用程序报文首先放入 socket buffer,socket buffer 空间受 wmem_default 限制,而 wmem_default 最大值受 wmem_max 限制。
调整方法:
sysctl -w net.core.wmem_max=xxxxxx ---- 限制最大值 sysctl -w net.core.wmem_default=yyyyyy
qdisc 队列长度
经过网络协议栈到达网络设备层的时候,报文从 socket buffer 转移至发送队列 qdisc。
调整方法:
ifconfig eth0 txqueuelen 10000
qdisc 权重
即一次 NET_TX_SOFTIRQ 软中断最大发送报文数,默认64。
调整方法:
sysctl -w net.core.dev_weight=600
tx descriptor 数量
即,多少个发送描述符。
调整方法:
ethtool -G eth0 rx 1024 tx 1024
高级特性:TSO/GSO/XPS
TSO(TCP Segmentation Offload):超过 MTU 大小的报文不需要在协议栈分段,直接由网卡分段,降低 CPU 负载。
GSO(Generic Segmentation Offload):TSO 的软件实现,延迟大报文分段时机到 IP 层结束或者设备层发包前,不同版本内核实现不同。
开启方法:
ethtool -K eth0 gso on ethtool -K eth0 tso on
XPS(Transmit Packet Steering):对于有多队列的网卡,XPS 可以建立 CPU 与 tx queue 的对应关系,对于单队列网卡,XPS 没啥用。
开启方法:
内核配置CONFIG_XPS 建立cpu与发送队列映射:echo cpu_mask > /sys/class/net/eth0/queues/tx-<n>/xps_cpus
ping 报文收取过程
驱动层
- 网卡驱动初始化,分配收包内存(ring buffer),初始化 rx descriptor
- 网卡收到报文,将报文 DMA 到 rx descriptor 指向的内存,并中断通知 CPU
- CPU 中断处理函数:关闭网卡中断开关,触发 NET_RX_SOFTIRQ 软中断
- 软中断调用网卡驱动注册的 poll 函数,收取报文(著名的 NAPI 机制)
- 驱动 poll 函数退出前将已经收取的 rx descriptor 回填给网卡
协议栈层
驱动层调用
napi_gro_receive()
,开始协议栈处理,对于 ICMP 报文,经过的处理函数:ip_rcv()
->raw_local_deliver()
->raw_rcv()
->__sock_queue_rcv_skb()
现在内核都使用 GRO 机制将驱动层 skb 上送协议栈,GRO 全称 Generic Receive Offload,是网卡硬件的 LRO 功能(Intel 手册使用 RSC 描述)的软件实现,可以将同一条流的报文聚合后再上送协议栈处理,降低 CPU 消耗,提高网络吞吐量。
- 送入 socket buffer
- 基于 poll/epoll 机制唤醒等待 socket 的进程
应用读取报文
- 从 socket buffer 的读取文,并拷贝到用户态
优化点
rx descriptor 长度
瞬时流量太大,软件收取太慢,rx descriptor 耗尽后肯定丢包。增大 rx descriptor 长度可以减少因为瞬时流量大造成的丢包。但是不能解决性能不足造成的丢包。
调整方法:
查看 rx descriptor 长度:ethtool -g eth0 调整 rx descriptor 长度:ethtool -G eth0 rx 1024 tx 1024
中断
为了充分利用多核 CPU 性能,高性能(现代)网卡一般支持多队列。配合底层 RSS(Receive-Side Scaling)机制,将报文分流到不同的队列。每个队列对应不同的中断,进而可以通过中断亲和性将不同的队列绑定到不同的 CPU。
PS:只有 MSI-X 才支持多队列/多中断。
调整方法:
中断亲和性设置:echo cpu_mask > /proc/irq/<irq_no>/smp_affinity 查看队列数:ethtool -l eth0 调整队列数:ethtool -L eth0
进阶:队列分流算法设置
NAPI
网卡中断中触发 NET_RX_SOFTIRQ 软中断,软中断中调用驱动 poll 函数,进行轮询收包。NAPI 的好处在于避免了每个报文都触发中断,避免了无意义的上下文切换带来的 Cache/TLB miss 对性能的影响。
但是 Linux 毕竟是通用操作系统,NAPI 轮询收包也要有限制,不能长时间收包,不干其他活。所以 NET_RX_SOFTIRQ 软中断有收包 budget 概念。即,一次最大收取的报文数。
收包数超过 netdev_budget(默认300)或者收包时间超过2个 jiffies 后就退出,等待下次软中断执行时再继续收包。驱动层 poll 收包函数默认一次收64个报文。
netdev_budget 调整方法:
sysctl -w net.core.netdev_budget=600
驱动层 poll 函数收包个数只能修改代码。一般不需要修改。
开启高级特性 GRO/RPS/RFS
GRO(Generic Receive Offload):驱动送协议栈时,实现同条流报文汇聚后再上送,提高吞吐量。对应
napi_gro_receive
函数。开启方法:
ethtool -K eth0 gro on
RPS(Receive Packet Steering):对于单队列网卡,RPS 特性可以根据报文 hash 值将报文推送到其它 CPU 处理(把报文压入其它 CPU 队列,然后给其它 CPU 发送 IPI 中断,使其进行收包处理),提高多核利用率,对于多队列网卡,建议使用网卡自带的 RSS 特性分流。
RFS(Receive Flow Steering):RFS 在 RPS 基础上考虑了报文流的处理程序运行在哪个 CPU 上的问题。比如报文流 A 要被运行在 CPU 2 的 APP A 处理,RPS 特性会根据报文 hash 值送入 CPU 1,而 RFS 特性会识别到 APP A 运行在 CPU 2 信息,将报文流 A 送入CPU 2。
PS: RFS 的核心是感知报文流的处理程序运行在哪个 CPU,核心原理是在几个收发包函数(
inet_recvmsg()
,inet_sendmsg()
,inet_sendpage()
、tcp_splice_read()
)中识别并记录不同报文流的处理程序运行在哪个CPU。RPS 开启方法:
echo cpu_mask > /sys/class/net/eth0/queues/rx-0/rps_cpus
RFS 设置方法:
sysctl -w net.core.rps_sock_flow_entries=32768 echo 2048 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
PS: RFS 有些复杂,这里只说举例配置,不讲述原理(其实就是不会。。。)
socket buffer
__sock_queue_rcv_skb()
中会将 skb 放入 socket buffer,其中会检查 socket buffer 是否溢出。所以要保证 socket buffer 足够大。设置方法:
sysctl -w net.core.rmem_max=xxxxxx ---- 限制最大值 sysctl -w net.core.rmem_default=yyyyyy
参考文档
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 | |
请作者喝杯咖啡吧 |