Linux下TCP收包过程

目录

前言

一、涉及的软硬件

二、收数准备

三、数据帧到达

四、数据帧从网卡到Ringbuffer

五、让CPU反应一下

1. 硬中断处理

2. 为啥关闭硬中断

六、从RingBuffer到softnet_data

七、从softnet_data到应用消费队列

1. 连接阶段Socket队列

2. 数据传输阶段Socket队列

八、应用消费数据

总结


前言

今天分享下Linux下网络收包过程,希望对Java小伙伴有所帮助。JVM屏蔽了诸多系统层面的细节,但屏蔽不了RD那颗越好奇越躁动的心。本文并没有许多C/C++的代码说明,更多站在流程、概念和监控的角度来行文,希望你能耐心看完。


一、涉及的软硬件

硬件:多核心CPU,支持多队列的网卡,内存,DMA控制单元

软件:能很好使用上述硬件的CentOS操作系统

二、收数准备

说明备注
上电启动
操作系统

初始化ksofrirq/x进程

以CPU核心为单位,每个CPU核心一个。该进程处理软中断
网络子系统
创建softnet_data数据结构每个CPU核心一个,数据结构上是一个队列
协议栈注册OS内核注册ip, tcp , udp数据包的处理函数
网卡部分
网卡驱动对网卡做基本的初始化
借助DMA控制单元向内存申请RingBuffer存储空间该RingBuffer数量与网卡配置的接收队列数量一致
网卡驱动向软中断向量表注册中断处理poll函数
上述准备完成后,网卡就可以准备接收数据了。

三、数据帧到达

首先在网卡缓存中排队,如果网卡支持数据包合并也会在缓存中做必要的合并,这里的包主要是数据链路层的小包到大包。注意包合并带来的是吞吐量的提升,但可能会造成收包延迟。

如何知道网卡名称?

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_queueserver端已发送syn,等待client ack
accept_queueclient ack完成,但尚未被应用调用accept取走

2. 数据传输阶段Socket队列

receive_queue待应用直接消费的数据
backlog_queuereceive_queue满,则放入backlog_queue中
unordered_queue收到的无序数据包,待重排序后放入前面某个队列中

查看tcp socket和收发队列长度

netstat 

特别地对于listen状态的socket,其Send-Q和Receive-Q代表的值为连接阶段socket相关。

八、应用消费数据

当应用从socketInputStream中读取数据,内核从队列中读取并喂给Java进程,进程完成应用级别的反序列化,完成数据接收。


总结

以上就是今天要讲的内容,本文仅仅简单介绍了TCP包的接收过程,其中涉及从网卡->RingBuffer, RingBuffer->Softnet_data, Softnet_data->Socket queue,Socket Queue->App buffer4次复制。希望对你在实际工作中有所帮助。个人能力有限,疏漏之处再所难免,欢迎批评指正!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值