目录
前言
一、涉及的软硬件
二、收数准备
三、数据帧到达
四、数据帧从 卡到Ringbuffer
五、让CPU反应一下
1. 硬中断处理
2. 为啥关闭硬中断
六、从RingBuffer到softnet_data
七、从softnet_data到应用消费队列
1. 连接阶段Socket队列
2. 数据传输阶段Socket队列
八、应用消费数据
总结
前言
一、涉及的软硬件
硬件:多核心CPU,支持多队列的 卡,内存,DMA控制单元
软件:能很好使用上述硬件的CentOS操作系统
二、收数准备
说明 | 备注 | |
上电启动 | ||
操作系统 | ||
初始化ksofrirq/x进程 |
以CPU核心为单位,每个CPU核心一个。该进程处理软中断 | |
络子系统 | ||
创建softnet_data数据结构 | 每个CPU核心一个,数据结构上是一个队列 | |
协议栈注册 | OS内核注册ip, tcp , udp数据包的处理函数 | |
卡部分 | ||
卡驱动对 卡做基本的初始化 | ||
借助DMA控制单元向内存申请RingBuffer存储空间 | 该RingBuffer数量与 卡配置的接收队列数量一致 | |
卡驱动向软中断向量表注册中断处理poll函数 | ||
上述准备完成后, 卡就可以准备接收数据了。 |
三、数据帧到达
首先在 卡缓存中排队,如果 卡支持数据包合并也会在缓存中做必要的合并,这里的包主要是数据链路层的小包到大包。注意包合并带来的是吞吐量的提升,但可能会造成收包延迟。
如何知道 卡名称p>
ifconfig
该命令可以查看 卡的特性,也可以查看当前MTU,IP地址等信息,不过最关键的还是 卡的设备名称。在后续的一系列命令中,还是要用的。
四、数据帧从 卡到Ringbuffer
卡驱动借助DMA在主存中完成空间分配,并将 卡缓存中的包以DMA方式送入主存的RingBuffer中。前面说到了多队列问题,这里 卡会根据配置计算该送入哪个RingBuffer中。
查看 卡接收队列数量
ethtool -l 设备名
查看 卡接收队列容量
ethtool -g 设备名
查看 卡特性
ethtool -k 设备名
查看 卡收发流量
sar -n DEV 1
如果RingBuffer空间太小,而对方发送速度很快,就会导致丢包。
在ifconfig命令中体现在 rx_overruns 指标上
在ethtool命令中体现在 rx_fifo_errors 指标上
卡完成数据复制到RingBuffer之后,产生硬中断,通知CPU核心有数据到达,当然也是通知与队列相关的CPU核心。
五、让CPU反应一下
CPU收到硬中断后,向CPU的待处理硬中断设备列表中记录 卡设备 ,并通过 卡驱动关闭硬中断。而后产生软中断,由软中断进程对软中断做进一步的处理。
1. 硬中断处理
硬中断处理分上半场和下半场。上半场记录设备 ,然后关闭硬中断,并产生对应的软中断,至此中断结束。下半场软中断处理才是真正的业务逻辑。总结来说,前者做了分发, 后者完成了真正的工作处理。
2. 为啥关闭硬中断
因为 络数据是源源不断到达,不停地被打断CPU没法专心做其他处理。既然已经中断一次就知道有数据到了,没有必要中断多次。
硬中断查看
cat /proc/interrupts
软中断查看
cat /proc/softirqs
CPU中断和进程亲和性绑定
/proc/irq/IRQ_NUMBER/smp_affinity
多核情况下,如果设置不当,可能存在 卡中断处理不均衡导致数据接收速度缓慢,可以手动均衡中断。也可以运行irqbalance进程让系统自动调整中断分布。
六、从RingBuffer到softnet_data
Ksoftirqd进程找到 卡注册的poll函数从RingBuffer中读取数据,首先转换为skb结构,然后并做必要的包合并,拷贝到softdata_net队列中等待CPU处理。如果队列已满,则会产生丢包,在ifconfig命令中体现在 rx_dropxx 指标上。
查看队列长度,默认为1000
sysctl net.core.netdev_max_backlog
查看队列状态
cat /proc/net/softnet_stat
到这里如果RingBuffer中没有可读数据,默认会再做一次轮训,如果依然没有数据,则调用 卡驱动恢复 卡硬中断。这里再做的一次轮训值得借鉴,可以缓解CPU的中断数量。
七、从softnet_data到应用消费队列
内核从softnet_data中取出数据并通过 络协议栈处理得到应用传输的原始数据,计算目标的Socket,并将其放入目标Socket的消费队列中。OS内核为将Socket分为两大类,一类是连接阶段的Socket,另一类则是数据传输阶段的Socket。显然,前者还处于TCP最开始的三次握手阶段。
1. 连接阶段Socket队列
syn_queue | server端已发送syn,等待client ack |
accept_queue | client ack完成,但尚未被应用调用accept取走 |
2. 数据传输阶段Socket队列
receive_queue | 待应用直接消费的数据 |
backlog_queue | receive_queue满,则放入backlog_queue中 |
unordered_queue | 收到的无序数据包,待重排序后放入前面某个队列中 |
查看tcp socket和收发队列长度
netstat
特别地对于listen状态的socket,其Send-Q和Receive-Q代表的值为连接阶段socket相关。
八、应用消费数据
当应用从socketInputStream中读取数据,内核从队列中读取并喂给Java进程,进程完成应用级别的反序列化,完成数据接收。
总结
文章知识点与官方知识档案匹配,可进一步学习相关知识 络技能树首页概览22968 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!