不知道是不是因为 GSO, GRO 是 Linux 新增特性的原因, 在 google 上找了半天都没有找到一篇详细的介绍如何使用 GSO/GRO 的文章, 最后从 Linux 内核中与 GSO/GRO 相关的 testcase 中窥到了一丝信息, 总结如下. 另外由于 GSO 是 5.0 新加的特性, 而且手头上也没有 linux 5.0 的机器, 所以如下总结并未实际验证过…

GSO, 关于 GSO 的原理, 参见 UDP segmentation offload 了解. 简单来说, 在业务发送 UDP 数据包时, 为了避免 IP 层对包进行分片, 一般会将待发送的 UDP 数据包的大小控制在 MTU - sizeof(udp header) - sizeof(ip header) 以下. 这里之所以要避免 IP 层分片, 主要是额外的 IP 层分片与重组有时候会导致不少问题, 比如同属于同一个上层数据包的多个 IP 分片在发送时, 任一分片的丢失都将导致整个上层数据包的重传. 再比如接收端设备为了完成 IP 重组不得不分配额外的内存等资源来存放管理当前已经收到的 IP 分片, 如果发送端发送大量的 IP 分片, 那么将会导致接收端用于暂存分片包的缓冲区被打满, 更糟糕的是如果发送端的一个数据包对应的分片数目过多, 那么接收端可能会一直无法完成一次完整的分片重组. 举个极端例子: 假设接收端 R 最多可以缓存 4 个 IP 分片包, 现在有发送端 S 发送了 1 个 8000 字节长度的 UDP 数据包, 在 MTU 为 1500 的情况下, 这个 UDP 数据包将拆分为 6 个分片. 很显然这时 R 将一直无法完成数据包的重组导致数据发送一直失败. 实际上我们在线上也确实遇到过类似的例子, 参见 IP 分片数目过多导致重组失败.

UDP GSO, 简单来说就是内核在接受到应用程序发来的一堆待发送的应用数据 D 之后, 会按照应用程序之前告诉内核的配置, 将接收到的 D 拆分为若干块, 之后为每一块加上 UDP header 封装成一个 UDP 数据包发送出去. 后面以 gsosize 来表示这里上层应用告诉内核的每块最大大小. 我们以 QUIC 协议为例, 当上层应用希望发送 2k 字节内容时, 在不使用 GSO 的情况下, QUIC 实现一般会将内容拆分存放到 2 个 QUIC packet 中, 之后对应着 2 个 UDP 数据包发送出去, 对应到伪代码如下. 很显然这涉及到两次 sendto 系统调用:

// 将待 1k 应用数据拼接成 QUIC packet 之后调用 sendto 发送
sendto(fd, "quic short packet header + frame header + [0, 1k) app data");
// 发送后 1k 应用数据
sendto(fd, "quic short packet header + frame header + [1k, 2k) app data");

在使用 GSO 之后, 应用需要首先通过 SOL_UDP/UDP_SEGMENT 告诉内核 gsosize 取值, 之后应用按照 gsosize 完成数据的组装:

// 分片1 总长度要限制为 gsosize.
part1 = "quic short packet header + frame header + app data"
// 分片2 存放着剩下的应用数据, 其大小可小于 gsosize.
part2 = "quic short packet header + frame header + app data"// 之后将分片1, 2 拼接成一个大缓冲区, 然后一次 sendto 调用完成数据发送.
// 除最后一个分片的中间分片的大小要固定为 gsosize.
sendto(fd, part1 + part2);
// 这里内核内部会按照 gsosize 再次将数据拆分为 '分片1', '分片2', 然后分为两个 UDP 数据包发送.

所以也可以看到 GSO 对于接收端来说是透明的. 毕竟 GSO 后发送的每一个 UDP 数据包都是一个完整的, 单独的数据包.

UDP GRO, 最开始我一直没有搞懂 UDP GRO, 单纯地从字面上看 GRO 是说内核会尽量在协议的最底层将收到的多个数据包拼接在一起之后向上传递, 也即上层看到的只是一个数据包. 对于 TCP 中的 GRO, 这里内核在拼接数据包时会遵循 TCP 的语义, 比如内核在收到了三个 TCP 数据包, TCP 序号分别为 33, 34, 35, 那么此时内核会将三个 TCP 数据包拼接成一个之后向上层协议传递, 这时还是可以理解的. 但是对于 UDP 而言, 大部分使用 UDP 作为传输协议的应用都依赖着 udp packet 边界的性质, 比如 QUIC short packet 中, packet header 中并没有长度字段, 完全是使用了 udp header 中的长度字段来隐式地指定了 short packet 的大小. 那这时 GRO 将多个 UDP 数据包拼接成一个之后, 上层应用还咋用这个边界信息来区分?

这个真的是 google 上找了半天, 现实中问了一圈大佬都没搞清楚, 知道最后快要弃疗的时候看到了内核关于 GRO/GSO 的单测 case 才大概了解了 UDP GRO 是如何拼接成, 很简单就是仅当 udp 数据包具有相同大小时, 才会被拼接成一个大的 udp 数据包, 同时内核还会告诉上层应用原始 udp 数据包的长度信息. 这样上层应用在需要的时候也可以根据这个信息来确定 udp packet 边界. 如 udpgso_bench_rx.c 所示, 当使用了 UDP GRO 之后, 应用程序的收包姿势如:

static int recv_msg(int fd, char *buf, int len, int *gso_size)
{char control[CMSG_SPACE(sizeof(uint16_t))] = {0};struct msghdr msg = {0};struct iovec iov = {0};struct cmsghdr *cmsg;uint16_t *gsosizeptr;int ret;iov.iov_base = buf;iov.iov_len = len;msg.msg_iov = &iov;msg.msg_iovlen = 1;msg.msg_control = control;msg.msg_controllen = sizeof(control);*gso_size = -1;ret = recvmsg(fd, &msg, MSG_TRUNC | MSG_DONTWAIT);if (ret != -1) {for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;cmsg = CMSG_NXTHDR(&msg, cmsg)) {if (cmsg->cmsg_level == SOL_UDP&& cmsg->cmsg_type == UDP_GRO) {gsosizeptr = (uint16_t *) CMSG_DATA(cmsg);*gso_size = *gsosizeptr;break;}}}return ret;
}

当 recv_msg() 返回后, [buf, len) 中存放的可能是多个 UDP 数据包拼接之后的内容, 此时 *gso_size 存放着原始 UDP 数据包的大小. 所以对于应用来说, 他可以以 *gso_size 来切分 buf 然后处理每一个数据包, 即中间位置的数据对应的原始 UDP 数据包大小总是为 *gso_size, 最后剩下的数据对应 UDP 数据包大小可能会小于 gsosize. 所以 QUIC 的实现 facebook/mvfst 处理 GRO 的姿势也就可以理解了:

void QuicServerWorker::onDataAvailable(const folly::SocketAddress& client,size_t len,bool truncated,OnDataAvailableParams params) noexcept {if { // 未使用 GRO.} else {  // 此时使用了 gro.  params.gro_ 存放着内核返回的 GRO 大小.size_t remaining = len;size_t offset = 0;while (remaining) {if (static_cast<int>(remaining) > params.gro_) {// 如上介绍所示: 位于中间位置的数据对应原始 UDP 数据包大小总是为 groauto tmp = data->cloneOne();tmp->trimStart(offset);tmp->trimEnd(len - offset - params.gro_);offset += params.gro_;remaining -= params.gro_;handleNetworkData(client, std::move(tmp), packetReceiveTime);} else {// 最后位置的数据对应原始 UDP 数据包大小可能不足 gro.data->trimStart(offset);remaining = 0;handleNetworkData(client, std::move(data), packetReceiveTime);}}}
}

UDP 与 GRO, GSO相关推荐

  1. 极速前进——DPDK GRO/GSO的转发性能提升实例

    通常,以太网的MTU是1500B,除去TCP/IP的协议首部,TCP的MSS(Max Segment Size)大小是1460B.一般情况下,协议栈会对超过1460B的TCP payload进行切片, ...

  2. linux 网卡gso,linux内核网络协议栈学习笔记:关于GRO/GSO/LRO/TSO等patch的分析和测试...

    TSO,全称是TCP Segmentation Offload,我们知道通常以太网的MTU是1500,除去TCP/IP的包头,TCP的MSS (Max Segment Size)大小是1460,通常情 ...

  3. IPv6的TSO/GRO/GSO及其Linux实现的不妥

    很明确的一件事是,IPv6不允许中间设备对报文分片.具体为什么这么设计,就是为了简单高效.因此,IPv6报头简洁了不少. 但TSO貌似并未违背取消IPv6分片的初衷,硬件把一些都处理的妥妥的,在路由软 ...

  4. 电商下一个风口——C2M重塑制造业

    过去十多年,电商行业走过了B2C和F2C等阶段,C2M将是业态进化的下一方向.上一轮消费互联网的革命,利用的是互联网和移动互联网技术,改变的是交易方式,提升的是交易效率.这一轮变革,利用的是5G.云. ...

  5. 网络协议栈TSO/UFO/GSO/LRO/GRO/RSS特性

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 网卡多队列技术与RSS功 ...

  6. TSO、GSO介绍与实现

    转载自:http://www.bsdmap.com/2012/02/22/转tso.ufo.gso.lro.gro和rss介绍/ 转载自:http://blog.chinaunix.net/uid-2 ...

  7. 如何快速优化 Linux 内核 UDP 收包效率? | CSDN 博文精选

    作者 | dog250 责编 | 郭芮 出品 | CSDN 博客 现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈 ...

  8. Linux内核UDP收包为什么效率低?能做什么优化?

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK. 但是,即便Linux内核协议栈收包 ...

  9. TSO与GSO简单区分

    概念 TSO(TCP Segmentation Offload): 是一种利用网卡来对大数据包进行自动分段,降低CPU负载的技术. 其主要是延迟分段.通过硬件(网卡)实现. GSO(Generic S ...

最新文章

  1. 2021年中寻找新SAP项目机会小记
  2. 北欧小国的宏大AI实验:让1%的人口接受人工智能培训
  3. fedora上ARM-LINUX-GCC 编译器安装
  4. 网络基准测试Netperf
  5. php PDO 浮点数返回,php – 如何在PDO中简单地返回对象?
  6. 持续集成之 Jenkins 钉钉通知(八)
  7. 最诡异数学悖论:1+1=1
  8. 何时使用hadoop fs、hadoop dfs与hdfs dfs命令(转)
  9. 【问题记录】阿里云轻量云服务器Ubuntu安装图形化桌面+远程连接
  10. tomcat多实例部署相关
  11. 从1.5K到18K 一个程序员的5年成长之路
  12. c++_iomanip头文件一些函数记录
  13. Atitit 多语言互相调用总结mltlan invk现在我们开发项目往往会采用多种语言,各取所长 组合使用。。常常需要互相调用为什么会调用多种语言?1.开发效率与可读性 ,一种情况是实现同
  14. 移动增值短信平台实施计划方案(珠海报业短信)
  15. wps的range对象
  16. 【裴礼文数学分析】例1.2.1
  17. dsp2812 linux开发板,dsp2812开发板自制编程流程
  18. 婚纱摄影、影楼、照相馆流量制造工具预约系统之种草社区
  19. win7如何设置wifi热点_教你win7如何设置网络共享文件夹
  20. Greasy Fork、GitHub、OpenUserJS

热门文章

  1. LambdaQueryWrapper中大于等于的方法
  2. Flash相册加载图片完毕等比缩放的类
  3. STM32通过SPI协议驱动OLED屏
  4. 《初雪之恋》唯美镜头
  5. 正则表达式常用规则:js正则 匹配 汉字、数字、英文字母、下划线 的 正则表达式
  6. irricht的水特效
  7. 倩女幽魂显示连接不上服务器,倩女幽魂手游闪退进不去 倩女幽魂手游连不上解决方法...
  8. w ndows10主题,从《Microsoft Store微软商店》获取精美免费Win10主题
  9. JFinal的BaseDao
  10. python爬取百度文库doc_Python百度文库爬虫之doc文件