用户态网络缓冲区
用户态网络缓冲区
缓冲区作用
用于临时存储数据以便高效地进行读写操作。用户态缓冲区位于用户空间中,与内核空间中的缓冲区(内核缓冲区)相对。
用户态接受缓存区
- 粘包问题,缓存非完整数据包
- 生产者的速度 > 消费者的速度 缓存没来得及处理的数据包
用户态发送缓冲区
- 可能一次不能把数据全部发送,缓存没有发送出去的数据
- 生产者的速度 > 消费者的速度
Linux系统收发数据包
接受数据包的过程
1.网卡收到数据包,通过DMA将数据写入内存(ringbuffer结构)
2.网卡向CPU发起硬件中断,CPU收到中断请求,根据中断表查找中断处理函数,调用中断处理函数
3.中断处理函数将屏蔽中断,发起软件中断
避免CPU频繁被网卡中断
使用软中断处理耗时操作,避免执行时间过长,导致CPU没法响应其他硬件中断
4.内核ksoftirqd线程负责软中断处理,该线程从ringbuffer中逐个取出数据帧到sk_buff
5.从帧头取出IP协议,判断是IPv4还是IPv6,去掉帧头,帧尾
6.从IP头看上一层协议是tcp还是udp,根据五元组找到socket,并将数据提取出来放到socket的接受缓冲区
软中断处理结束后开启硬件中断
7.应用程序通过系统调用将socket的接收缓冲区的数据拷贝到应用层缓冲区
发送数据包的过程
1.应用程序通过系统调用将用户数据拷贝sk_buffer并放到socket的发送缓冲区(udp不使用发送缓冲区)
2.网络协议栈从socket的发送缓冲区取出sk_buff,并克隆出一个新的sk_buffer(tcp支持丢失重传)
3.向下传递依次增加TCP/UDP头部,IP头部,帧头(MAC头部),帧尾(tcp分段,ip分片)
TCP 分段是为了根据应用的需要,控制数据流的传输。
IP 分片是为了保证数据包大小符合网络层的传输限制。
4.触发软中断通知网卡驱动程序,有新的网络包需要发送
5.网卡驱动程序从发送队列依次取出sk_buff写ringbuffer(内存DMA区域)
6.触发网卡发送,发送成功,触发硬件中断,释放sk_buff和ringbuffer内存
tcp对应的是克隆而来的
udp对应的是原始的
7.当收到tcp报文的ack应答时,将释放原始的sk_buff。
TCP/UDP设计是否一样
基本一样,有以下的区别
TCP
- 基于流,可靠传输。
- 需要粘包处理。
- 支持TCP分段。
- 支持IP分片和重组。
- 内核中有接收缓冲区
UDP
- 基于报文,不可靠传输
- 只负责发送,不关心接收。
- 没有发送缓冲区。
- 最大报文大小为64K。
Reactor、Proactor 模型
- Reactor:通过 IO 多路复用检测缓冲区就绪,使用事件驱动模型通知用户态调用
read
。 - Proactor:异步投递请求,内核拷贝数据后,通过完成通知回调用户态。
实现思想
定长 buffer
因为每次取出数据后,都需要对齐,需要将数据腾挪到头部。
缺点:
- 内存浪费
- 伸缩性差
- 频繁腾挪数据
ringbuffer
利用头尾指针解决了数据导入时的对齐问题
缺点:
- 内存浪费
- 伸缩性差
- 数据离散
chainbuffer(块状链表)
misalign:从开头以及取走的长度,下次从开头加上misalign就是数据开始点
offset:有效数据长度
- 不需要腾挪数据
- 动态扩缩容且无需数据拷贝
- 造成不连续空间,可能引发多次系统调用