_Linux网络数据包的揭秘以及常见的调优方式总结

2023-11-19

作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供服务。SRE 需要对服务器数据包的接收和发送路径有全面的了解,以方便在服务异常时能快速定位问题。

以 tcp 协议为例,本文将对 Linux 内核网络数据包接收的路径进行整理和说明,希望对大家所有帮助。

Linux 数据包接收路径的整体说明

接收数据包是一个复杂的过程,涉及很多底层的技术细节 , 这里先做一下大概的说明 :

NIC (network interface card) 在系统启动过程中会向系统注册自己的各种信息,系统会分配专门的内存缓冲区,

NIC 接收到数据包之后,就会存放在内存缓冲区,通过硬件中断通知内核有新的数据包需要处理 .

内核从缓冲区取走 NIC 接收过来的数据,交给 TCP/IP 协议栈处理。

内核的 TCP/IP 协议栈代码进行处理后,更新协议的各种状态,然后交给应用程序的 socket buffer。

然后应用程序就可以通过 read() 系统调用,从对应的 socket 文件中,读取数据。

对内核数据包接收的路径做一下分层,总体可分为三层 :

  1. 网卡层面
  2. 1.1 网卡接收到数据包
  3. 1.2 将数据包从网卡硬件转移到主机内存中 .
  4. 内核层面
  5. 2.1 TCP/IP 协议逐层处理
  6. 应用程序层面
  7. 3.1 应用程序通过 read() 系统调用 , 从 socket buffer 读取数据

如下图 :

接下来解释一下什么是 NAPI

什么是 NAPI

系统启动时会为网卡分配 Ring Buffer (环形缓冲区 ), Ring Buffer 放的是一个个 Packet Descriptor(数据包描述符),是实际数据包的指针。实际的数据包是存放在另一块内存区域中(由网卡 Driver 预先申请好),称为 sk_buffers, sk_buffers 是可以由 DMA(https://en.wikipedia.org/wiki/DMA) 直接访问的 .

Ring Buffer 里的 Packet Descriptor ,有两种状态:ready 和 used 。初始时 Descriptor 是空的,指向一个空的 sk_buffer,处在 ready 状态。当有数据时,DMA 负责从 NIC 取数据,并在 Ring Buffer 上按顺序找到下一个 ready 的 Descriptor,将数据存入该 Descriptor 指向的 sk_buffer 中,并标记 Descriptor 为 used。因为是按顺序找 ready 的 Descriptor, 所以 Ring Buffer 是个 FIFO 的队列。

内核采用 struct sk_buffer(https://elixir.bootlin.com/linux/v4.4/source/include/linux/skbuff.h#L545) 来描述一个收到的数据包, sk_buffer 内有个 data 指针会指向实际的物理内存。

当通过 DMA 机制存放完数据之后,NIC 会触发一个 IRQ(硬件中断) 让 CPU 去处理收到的数据。因为每次触发 IRQ 后 CPU 都要花费时间去处理 Interrupt Handler,如果 NIC 每收到一个 Packet 都触发一个 IRQ 会导致 CPU 花费大量的时间执行 Interrupt Handler,而每次执行只能从 Ring Buffer 中拿出一个 Packet,虽然 Interrupt Handler 执行时间很短,但这么做非常低效,并会给 CPU 带来很多负担。所以目前都是采用一个叫做 New API(NAPI)(https://wiki.linuxfoundation.org/networking/napi) 的机制,去对 IRQ 做合并以减少 IRQ 次数,目前大部分网卡 Driver 都支持 NAPI 机制。NAPI 机制是如何合并和减少 IRQ 次数的 , 可以简单理解为: 中断 + 轮询 。在数据量大时,一次中断后通过轮询接收一定数量数据包再返回,避免产生多次中断 , 具体细节大家可以参考这篇文章 (https://ylgrgyq.github.io/2017/07/23/linux-receive-packet-1/).

概括一下网卡层面整个数据包的接收过程:

  1. 驱动程序事先在内存中分配一片缓冲区来接收数据包 , 叫做 sk_buffers.
  2. 将上述缓冲区的地址和大小(即数据包描述符),加入到 rx ring buffer。描述符中的缓冲区地址是 DMA 使用的物理地址 ;
  3. 驱动程序通知网卡有新的描述符 (或者说有空闲可用的描述符 )
  4. 网卡从 rx ring buffer 中取出描述符 , 从而获取缓冲区的地址和大小 .
  5. 当一个新的数据包到达,网卡 (NIC) 调用 DMA engine,把数据包放入 sk_buffer.

如果整个过程正常 , 网卡会发起中断,通知内核的中断程序将数据包传递给 IP 层,进入 TCP/IP 协议栈处理。

每个数据包经过 TCP 层一系列复杂的步骤,更新 TCP 状态机,最终到达 socket 的 recv Buffer,等待被应用程序接收处理。

然后 , 内核应该会把刚占用掉的描述符重新放入 ring buffer,这样网卡就可以继续使用描述符了。

我们可以使用 ethtool 命令,进行 Ring Buffer 的查看和设置 .

1 查看网卡当前的设置(包括Ring  Buffer): ethtool -g eth12 改变Ring Buffer大小: ethtool -G eth1 rx 4096 tx 4096

四 中断处理程序如何把数据包传递给网络协议层

我们通过一张图来说明下 ,

上图中涉及到非常多的技术细节,限于篇幅我们只做总体的说明 :

  1. NIC 发起的硬件中断(也称为中断处理的上半部),被内核执行之后,开启了软中断(中断处理的下半部),并马上退出硬件中断处理程序 , 以便其他硬件可以继续发起硬件中断 .
  2. 软中断处理程序中,通过 poll 循环把数据从 Ring Buffer 取走,传给网络协议层处理,然后重新开启之前已经禁用的网卡硬件中断 .
  3. 当有新的数据包到达网卡时 , 回到第 1 步 .

这里有几点需要额外说明 :

什么是中断处理的上半部和下半部

我们知道中断随时可能发生,因此中断处理程序也就随时可能执行。所以必须保证中断处理程序能够快速执行,这样才能尽快恢复被中断的代码。因此尽管对硬件而言,操作系统能迅速对其中断进行服务非常重要,而对于系统其他部分而言,让中断处理程序尽可能在短时间内完成运行也同样重要。所以我们一般把中断处理切为 2 个部分,上半部在接收到一个中断时立刻开始执行,但他只做必要的工作,例如对接收的中断进行应答或复位硬件,这些工作都是在所有中断被禁止的情况下完成的。而那些允许被稍后执行的工作,都会推到下半部去,下半部并不会马上执行,而是会在稍后适当的时机执行。

网卡的软中断处理

现在的网卡基本都支持 RSS(Receive Side Scaling)(https://en.wikipedia.org/wiki/Network_interface_controller#RSS),也就是多对列技术。一张网卡有多个队列,每个队列都有各自的 IRQ 号和 Ring Buffer,但是默认情况下网卡的软中断都是在 CPU0 上处理,在流量大的时候,会造成 CPU0 负载打满,引起丢包. 我们可以通过绑定中断和 CPU 的亲和性,把中断处理均衡到多核心上 (https://www.vpsee.com/2010/07/load-balancing-with-irq-smp-affinity/),提升系统整体性能 .

什么是 RPS

RPS 全称是 Receive Packet Steering, 采用软件模拟的方式,实现了多队列网卡所提供的功能,分散了在多 CPU 系统上数据接收时的软中断负载, 把软中断分到各个 CPU 处理,而不需要硬件支持,在多核 CPU 和单队列网卡的情况下,开启 RPS 可以大大提升网络性能 .

如果系统开了 RPS, 数据包会被缓冲在 TCP 层之前的队列中 , 我们可以通过 net.core.netdev_max_backlog 适当加大这个队列的长度,以保证上层的处理时间 .

TCP/IP 协议栈层面

此时数据包已经接入内核处理区域,由内核的 TCP/IP 协议栈处理

(一) 连接建立

大家知道,两个基于 tcp 协议的 socket 要通信,首先要进行连接建立的过程,然后才是数据传输的过程。

我们先简单看下连接的建立过程,客户端向 server 发送 SYN 包,server 回复 SYN+ACK,同时将这个处于 SYN_RECV 状态的连接保存到半连接队列。客户端返回 ACK 包完成三次握手,server 将 ESTABLISHED 状态的连接移入 accept 队列,等待应用调用 accept()。

可以看到建立连接涉及两个队列:

  • 半连接队列 (SYN Queue): 保存 SYN_RECV 状态的连接。队列长度由 net.ipv4.tcp_max_syn_backlog 设置
  • 完整连接队列 (ACCEPT Queue): 保存 ESTABLISHED 状态的连接。队列长度为 min(net.core.somaxconn, backlog)。其中 backlog 是我们创建 ServerSocket(int port,int backlog) 时指定的参数,最终会传递给 listen 方法:
#includeint listen(int sockfd, int backlog);

如果我们设置的 backlog 大于 net.core.somaxconn,完整连接队列的长度将被设置为 net.core.somaxconn。

注意:不同的编程语言都有相应的 socket 申请方法 , 比如 Python 是 socket 模块.在服务端监听一个端口,底层都要经过 3 个步骤:

申请 socket、bind 相应的 IP 和 port、调用 listen 方法进行监听。这个 listen 方法 python 会进行封装,别的编程语言也会进行封装,但最终都是调用系统的 listen() 调用

我们对这两个队列做一下总结 :

(二) 数据传输

连接建立后 , 就到了 socket 数据传输的层面。此时 kernel 能够为应用程序做的,就是通过 socket Recv Buffer 缓存数据 , 尽量保证上层处理时间 .

1 Recv Buffer 自动调节机制

kernel 可以根据实际情况,自动调节 Recv Buffer 的大小 , 以期找到性能和资源的平衡点 .

当 net.ipv4.tcp_moderate_rcvbuf 设置为 1 时,自动调节机制生效,每个 TCP 连接的 recv Buffer 由下面的 3 元数组指定 (min, default, max):

net.ipv4.tcp_rmem = 4096    87380   16777216

最初 Recv Buffer 被设置为 87380,同时这个缺省值会覆盖 net.core.rmem_default 的设置 , 随后 recv buffer 根据实际情况在最大值和最小值之间动态调节。

当 net.ipv4.tcp_moderate_rcvbuf 被设置为 0,或者设置了 socket 选项 SO_RCVBUF,缓冲的动态调节机制被关闭。

如果缓冲的动态调节机制被关闭 , 同时 socket 自己也没有设置 SO_RCVBUF 选项,那么一个 socket 的默认 Buffer 大小将由 net.core.rmem_default 决定,但是应用程序仍然可以通过 setsockopt() 系统调用,加大自己的 Recv Buffer, 最大不能超过 net.core.rmem_max 的设定 .

因此,我们可以得出如下总结 :

  • 没有特殊情况 , 建议打开 net.ipv4.tcp_moderate_rcvbuf=1, 这样 kernel 会自动调整每个 socket 的 Recv Buffer
  • 我们应该把 net.ipv4.tcp_rmem 中 max 值和 net.core.rmem_max 值设置成一致,这样假设应用程序没有关注到这个点,仍然可以由 kernel 把它自动调节成系统最大的 Recv Buffer.
  • Recv Buffer 的默认值可以适当进行提高 , 包括 net.core.rmem_default 和 net.ipv4.tcp_rmem 中的 default 设置 , 以更加激进的方式传输数据 .

关于 Linux 接收数据包链路优化的整体总结

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

_Linux网络数据包的揭秘以及常见的调优方式总结 的相关文章

  • 第三章. Pandas入门—索引设置

    第三章 Pandas入门 3 8 索引设置 1 索引的作用 1 更方便的查询数据 2 使用索引可以提升查询性能 如果索引是唯一的 Pandas会使用哈希表优化 查找数据的时间复杂度为O 1 如果索引不是唯一的 但是有序 Pandas会使用二
  • 梯度下降函数理解

    r d 可以理解为有d的参数进行约束 或者 D 向量有d个维度 咱们将楼主的给的凸优化结构细化一点 别搞得那么抽象 不好解释 其中 咱们可以令 f ok 这个先介绍到这里 至于f x 为什么用多项式的方式去模拟 相信也是很多人的疑问 很简单
  • 组织关系图谱

    div style width 100 height 800px div
  • git强制提交本地分支覆盖掉远程分支

    语法比较简单 命令如下 git push origin 分支名 force 举个栗子 git push origin V2 2 3 force 运行结果 Total 0 delta 0 reused 0 delta 0 To http 19
  • golang-bufio 缓冲扫描

    前面两篇博客 介绍了 bufio 包中的缓冲读和写 bufio go 下面再来介绍一下缓冲扫描 scan go 这个扫描的是用来对缓存读的更高级封装 提供了一些更易用的方法 缓冲扫描 Scanner 提供了一个方便的接口来读取数据 例如使用
  • flask-会话机制

    使用flask bootstrap 步骤 1 pip install flask bootstrap 2 进行配置 from flask bootstrap import Bootstrap bootstrap Bootstrap 在 in
  • kafka 监控工具--CMAK

    CMAK previously known as Kafka Manager is a tool for managing Apache Kafka clusters See below for details about the name
  • 二分法总结(超级详细)附带图解

    文章目录 1 二分法 2 时间复杂度 3 二分法的套路 3 1 整数的二分 3 2 实数的划分 四 相关习题 4 1 数的范围 4 2 数的三次方根 1 二分法 二分查找是一个时间效率极高的算法 尤其是面对大量的数据时 其查找效率是极高 时
  • python读取npy文件时,太大不能完全显示,其解决方法

    python读取npy文件时 太大不能完全显示 其解决方法 当用python读取npy文件时 会遇到npy文件太大 用print函数打印时不能完全显示 如以下情况 其解决办法是 添加一行代码 np set printoptions thre
  • 2023汽车行业数字化转型报告

    目前 汽车行业正经历百年未有之大变局 在新一轮科技革命以及减碳 能源形势变革智能化变革推动下 汽车产业正由功能时代向智能时代演进 由 以车为中心 向 以用户为中心 转变 汽车的产品属性 产业价值链和生态结构都面临被颠覆 新的汽车市场格局正在
  • Python爬虫从入门到精通:(33)scrapy中间件_Python涛哥

    中间件 作用 批量拦截请求和响应 爬虫中间件 下载中间件 推荐 拦截请求 篡改请求url 伪装请求头信息 UA Cookie 设置代理 重点 拦截响应 篡改响应数据 详解 我们创建个工程middlePro 爬取百度和搜狗 import sc
  • goto编程练习

    for 的初始化要放到JUMP上边 不然i会一直为1 for 的i 也不能放到括号里边 不然i永远为0 1 include
  • 200smart第二课基本编程

    一 程序块 主程序main和子程序 主程序是执行程序的入口 没有主程序就不知道程序从哪里开始 子程序是一个大型程序中的某个代码 一般是完成某个算法 二 符号表 给变量定义 当我们在编程的时候 需要定义一些符号名称 如下图 程序运行 注释使程
  • MFC重载鼠标停留WM_MOUSEHOVER和离开WM_MOUSELEAVE消息

    1 重载OnMouseMove 消息 在消息的实现中添加代码 void CMainWindow OnMouseMove UINT nFlags CPoint point TRACKMOUSEEVENT tme tme cbSize size
  • 爬虫与反爬虫技术简介

    互联网的大数据时代的来临 网络爬虫也成了互联网中一个重要行业 它是一种自动获取网页数据信息的爬虫程序 是网站搜索引擎的重要组成部分 通过爬虫 可以获取自己想要的相关数据信息 让爬虫协助自己的工作 进而降低成本 提高业务成功率和提高业务效率
  • @JSONField 解决json字符串转对象,对象属性名与json中key不一致,如何接收数据问题

    背景 我有个对象 过来个json 想用这个对象接收json中的值 对象中属性名与json中key值不一致 实现 这个时候 JSONField注解就派上用场了 不能直接放在属性上 要放在set方法上 模拟 1 搞个对象 属性名分别为name
  • 【靶场】upload-labs Pass-02

    考纲 本pass在服务端对数据包的MIME进行检查 在右上角点击 查看提示 中看到 一 上一关 靶场 upload labs Pass 01 二 大马 介绍两款 php 大马 因为 一句话木马看不上 如果师傅有其他好用的 大马 还望师傅在评
  • QT添加qss文件和资源文件

    先右键项目 选择 Add New 选择一个模板 选择 Qt 模板 再选择 Qt Resource Files 点击 Choose 填上资源文件的名称 默认添加项目路径下 后面的步骤默认即可 点击完成 新建完成了资源文件后 默认会进入 res
  • 运放稳定性连载21:电容性负载的稳定性——具有双通道反馈的RISO(2)

    现在 我们必须测量如图10 6所示的Zo 小信号AC开环输出阻抗 该Tina SPICE测试电路将测试空载OPA177的Zo R2和R1以及LT为低通滤波器函数提供了一条AC通道 这样 使得我们能将DC短路和AC开路一起并入反馈电路 DC工
  • ssh报错no key alg(关于低版本连接高版本ssh)

    高版本 8 4 低版本 4 3 按照网上的方法试过 通过ssh keygen命令重新生成ssh主机秘钥 可以不用重启sshd服务 ssh keygen t rsa f etc ssh ssh host rsa key ssh keygen

随机推荐

  • NoReverseMatch: Reverse for ‘data‘ not found . ‘data‘ is not a valid view function or pattern

    Django gt python manage py runserver时报错 NoReverseMatch Reverse for data not found data is not a valid view func tion or
  • 制作一辆“自动驾驶”平衡自行车需要用到哪些知识

    目录 先看下小车效果 小车电路设计 相关软件工具 keil C语言设计编码调试工具 主要 mcuisp 代码烧录工具 一般使用一种烧录工具就可以 STM32 STlink stlink烧录工具 STM32 Cube pro 烧录工具 ope
  • C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现

    本文是转载的 正版是https blog twofei com 496 欢迎去看正版 C 中的虚函数 表 实现机制以及用C语言对其进行的模拟实现 前言 大家都应该知道C 的精髓是虚函数吧 虚函数带来的好处就是 可以定义一个基类的指针 其指向
  • OceanBase使用范例

    http www mysqlops com 2011 08 31 oceanbase use html OceanBase的使用类似于关系型数据库 需要预先创建schema 关于schema的格式 请参见schema说明 假如我们有以下sc
  • c#Socket 异步通讯(客户端与服务端)

    c Socket 异步通讯 多个客户端与服务端 最近公司有个项目 涉及到的通讯对象有点多 就拿其中一个库的通讯来说就用到了3个PLC 这里就涉及了一个服务器与多个客户端之间的通讯了 同时上位机既需要做客户端 也需要做服务端 因为跟PLC之间
  • HTTP响应详解, HTTP请求构造及HTTPS详解

    HTTP响应详解 认识 状态码 status code 状态码表示访问一个页面的结果 是访问成功 还是失败 还是其他的一些情况 以下为常见的状态码 200 OK 这 是一个最常见的状态码 表示访问成功 抓包抓到的大部分结果都是 200 例如
  • numpy load npz文件

    一 问题 numpy version 1 23 0 优化项目的是时候发现索引一个dict的时候很慢 因此进行分析 速度很慢的问题代码如下 arr dict np load test npz npz 100MB for i in range
  • 神州网信远程、关闭屏幕时间、关闭神州网信密码

    一 远程查看电脑 按 windows r 输入gpedit msc 运行组策略 gpedit msc 进行下面的操作 1 计算机配置 管理模板 Windows组件 远程桌面服务 远程桌面会话主机 连接 允许用户通过使用远程桌面服务进行远程连
  • Qt creator4.8.0 以上使用SqLite数据库进行数据操作

    文章目录 前言 一 在 pro工程文件中添加sql模块 二 使用步骤 1 添加头文件 2 链接并打开数据库 3 创建用户信息表management info 4 插入数据操作 5 修改数据库操作 6 查询数据库 总结 前言 Qt creat
  • 基于MATLAB的filter的使用,低通、带通和高通滤波器设计

    1 目的 学习MATLAB的filter函数的使用 通过设计低通 带通和高通滤波器对其进行仿真 2 用到的主要函数和工具 MATLAB FDATOOL filter fft 3 设计 信号的产生 Parameter Interface Fr
  • java高级开发面试题总结

    面试题总结 JAVA高级工程师 近期考虑换工作的问题 于是投简历面试 面试5家公司的高级Java工程师 有4家给了我offer 想着总结一下面试经验 方便最近正在寻求机会的你们 一 无笔试题 不知道是不是职位原因还是没遇到 面试时 都不需要
  • 阿里云轻量应用服务器使用指南适用于所有人

    最近一直在捣鼓阿里云服务器 想着把自己写好的一些项目部署到服务器上供其他人访问 一路上踩了不少坑 也查了不少资料 最后解决了 写个博客记录下来 也为其他想要建站的同学提供一个指引 购买轻量应用服务器 传送门 阿里云 如果是在校学生 可以直接
  • SpringCloud之Hystrix

    1 服务熔断与降级 在微服务架构中多层服务之间会相互调用 如果其中有一层服务故障了 可能会导致一层服务或者多层服务 故障 从而导致整个系统故障 这种现象被称为服务雪崩效应 SpringCloud 中的 Hystrix 组件就可以解决此类问题
  • 有时间再看decode详解

    Oracle 中 decode 函数用法 含义解释 decode 条件 值1 返回值1 值2 返回值2 值n 返回值n 缺省值 该函数的含义如下 IF 条件 值1 THEN RETURN 翻译值1 ELSIF 条件 值2 THEN RETU
  • 冲刺春招-精选笔面试 66 题大通关 day6

    day6题目 33 搜索旋转排序数组 54 螺旋矩阵 bytedance 006 夏季特惠 学习计划链接 冲刺春招 精选笔面试 66 题大通关 今日知识点 二分 模拟 01背包 难度为中等 中等 字节 简单 33 搜索旋转排序数组 整数数组
  • ARouter(二)源码解析

    前言 这一篇我们来具体看一下ARouter的实现原理 如果你之前没有接触过ARouter 可以先阅读上一篇 Android 从零开始打造自己的深度链接库 一 ARouter简介 废话不多 我们赶紧分析源码 正文 首先我们从github下载最
  • 中文信息处理实验8——基于逻辑斯蒂回归模型的文本分类

    目录 实验目的 实验要求 实验内容及原理 参考代码 实验结果 实验目的 加深对汉语文本信息处理基础理论及方法的认识和了解 锻炼和提高分析问题 解决问题的能力 通过对具体项目的任务分析 数据准备 算法设计和编码实现以及测试评价几个环节的练习
  • win10系统C盘出现感叹号及加密图标解除

    近期遇到Win10系统C盘图标加密情况 经过搜索查找最终解决 并对操作进行简单记录 1 以管理员身份打开命令行窗口 2 输入 manage bde off c 3 相关指令 加密指令 manage bde on c 查看状态指令 manag
  • 使用定时框架Quartz.net时,发布到服务器后无法正常执行定时任务

    问题描述 使用Quartz net每天定时执行某个任务时 未能正常执行 每次在本地测试时 设置了短的时间间隔 都能正常执行任务 但是挂到服务器后 设置定时执行时间为几个小时 却不能正常执行我们要执行的操作 原 因 IIS的程序池有一个闲置超
  • _Linux网络数据包的揭秘以及常见的调优方式总结

    作为业务 SRE 我们所运维的业务 常常以 Linux TCP UDP daemon 的形式对外提供服务 SRE 需要对服务器数据包的接收和发送路径有全面的了解 以方便在服务异常时能快速定位问题 以 tcp 协议为例 本文将对 Linux