Linux 网络第一部分:内核网络栈
点击此处查看最新的网赚项目教程
Linux Networking Part 1 : Kernel Net Stack
系列介绍
首先,从这里下载未编译的 Linux 内核代码非常方便:
在这个系列中,我们将探讨服务器世界中的网络方式及其演变过程,从使用传统的 Linux 内核网络栈到使用 OVS 进行网络虚拟化,再到使用 NFV 和 SR-IOV 处理电信负载。
本文概览
下面的图表只是简要地展示了数据包在 Linux 内核中的处理过程,如果你想大致了解一下,可以浏览一下;但如果你想了解更深入、更实用的细节,请继续阅读。
第一部分:Linux 网络栈
在本文中,我们将遵循 Jiri Bina 在 2018 年 DevConf CZ 大会上的演讲,来介绍 Linux 内核中 IPv4/TCP 流量的基本流程。他在演讲中非常精彩地展示了 Linux 内核中整个 OSI 七层模型的数据包流程。在我们深入了解流程之前,有一些辅助工具和概念我们需要熟悉:
1) Ring Buffers
在网卡(NIC)设备启动并由内核加载其驱动模块时,驱动程序会首先分配接收(Rx)和发送(Tx)队列或缓冲区,这些在设备内存中被称为环形缓冲区(Ring Buffers),通常位于内核空间的 DMA 部分。你可以检查这些缓冲区的最大值和配置大小:
# ethtool -g INTERFACE_NAME
Ring parameters for ens192:
Pre-set maximums:
RX: 4096 <<<<<<<<<<<<<<<, Max size in bytes
RX Mini: 2048
RX Jumbo: 4096
TX: 4096 <<<<<<<<<<<<<<<, Max size in bytes
Current hardware settings:
RX: 1024 <<<<<<<<<<<<<<<, Configured size in bytes
RX Mini: 128
RX Jumbo: 512
TX: 512 <<<<<<<<<<<<<<<, Configured size in bytes
在早期版本的内核中,每当一个数据包到达这些缓冲区时,会向 CPU 触发一个硬件中断,这种做法非常耗费资源。不过,幸运的是,引入了 NAPI 来解决这个问题,下面你将更详细地了解它。
2) Socket 缓冲区(sk_buff)
sk_buff(Socket 缓冲区),Linux 通过 Socket 缓冲区(sk_buff)与数据包/数据段(或其他接收到的网络数据)进行交互,每个 sk_buff 的数据和元数据(头部)是分开处理的,因此内核不需要在内存中移动数据包。Socket 缓冲区像一个桶,它们保存接收到的数据直到处理完成。Socket 缓冲区不会随着数据包的处理而被销毁,它们会被释放并重新分配给新的数据包。当这些缓冲区被使用时,CPU 会创建新的 sk_buff(新的桶)来处理额外的流量。
旁注:“sk_buff”和“skb”可以互换使用,而在内核代码中你会发现 skb 被广泛使用。sk_buff 包含以下内容(如下图所示):
Interface (input_dev):指的是数据包到达的接口名称。
Protocol :如 IPv4、IPv6 等。
Head :指向 sk_buff 的起始位置,实际上从一个空闲空间开始,为额外的头部(例如 VLAN 标签)留出空间。
data :数据指针并不指示数据的起始位置,而是在堆栈函数中动态使用以弹出和推入头部。例如,在内核中当你弹出以太网头部时,实际上只是将数据指针移动到 IP 头部的起始位置,因此没有物理上弹出头部。
tail :指向数据部分的末尾和 sk_buffer 空闲部分的起始位置。这个空闲部分用于适应不同大小的数据包,因为 sk_buffer 的大小是根据配置的 MTU 设置的。
end :指向 sk_buff 在内存中的结束位置。
MAC & IP & TCP header,指针始终存储在 sk_buff 的元数据中,这使得可以直接调用它们,而无需在 sk_buff 上执行弹出和推入操作。
cloned :sk_buff 的头部可能被克隆,但数据不会被克隆。(即skb可能被复制)
注意:数据包在内核中不会被复制。实际的数据包数据保留在数据包缓冲区中。每次克隆或复制时,数据包缓冲区保持不变,而是创建一个新的 sk_buff(即 SKB),即新的元数据指向现有的数据包缓冲区。虽然在内核中没有复制数据包,但当数据包到达应用程序时会被复制,此时 SKB 被释放。
3) 内核中断 (IRQ 与 SoftIRQ)
简单来说,中断用于让 CPU 停止当前的工作而去处理中断请求的部分(即执行中的服务程序)。中断有多种模型,每种模型包含许多类型,但基本上可以分为两大类。
$ cat /proc/interrupts
## These are the hardware interrupts, including the IRQ ID , the CPU and the number of interrupts of this type that was triggered .
CPU0 CPU1 CPU2 CPU3 CPU4
0: 28 0 0 0 0 IO-APIC 2-edge timer
1: 0 0 0 0 0 IO-APIC 1-edge i8042
8: 0 0 0 0 0 IO-APIC 8-edge rtc0
9: 0 0 0 0 0 IO-APIC 9-fasteoi acpi
12: 0 0 0 0 0 IO-APIC 12-edge i8042
14: 0 0 0 0 0 IO-APIC 14-edge ata_piix
15: 0 0 0 0 0 IO-APIC 15-edge ata_piix
每个中断(硬件中断)由一个向量标识,该向量是一个字节的标识符,范围为 0-255。其中 0-31 被称为异常(不可屏蔽)中断,范围 32-47 为可屏蔽中断,48 到 255 分配给软件中断(SoftIRQ)。
简而言之,你在上面的输出中会遇到三种类型的硬件中断:MSI-X、MSI 和传统 IRQ。简单来说,MSI 代表消息信号中断,它取代了旧的方式,即为每个设备在 CPU 插槽中使用一个物理引脚处理中断。你可以在这里阅读有关 MSI 和其他类型硬件中断的更多信息:消息信号中断。此外,关于硬件中断的更多信息,可以查阅这篇很棒的论文:“Linux 中断:基本概念”()。
# SoftIRQs queues process
$ ps aux | grep ksoftirqd
root 14 0.0 0.0 0 0 ? S 22:56 0:00 [ksoftirqd/0]
root 23 0.0 0.0 0 0 ? S 22:56 0:00 [ksoftirqd/1]
root 29 0.0 0.0 0 0 ? S 22:56 0:00 [ksoftirqd/2]
root 35 0.0 0.0 0 0 ? S 22:56 0:00 [ksoftirqd/3]
root 41 0.0 0.0 0 0 ? S 22:56 0:00 [ksoftirqd/4]
# Monitoring the Rx and Tx buffers :
$ watch -n1 grep RX /proc/softirqs
Every 1.0s: grep RX /proc/softirqs
NET_RX: 0 2 0 122
$ watch -n1 grep TX /proc/softirqs
Every 1.0s: grep TX /proc/softirqs
NET_TX: 0 0 0 0
3) 其他概念网络流程简介
图 B:对内核中分配的内容及数据包如何通过内核的简单解释:
在内核启动非常早期的时候,CPU 分配数据包缓冲区(接收 RX 和发送 TX 缓冲区),并构建文件描述符。
CPU 通知 NIC 新的描述符已经创建,以便 NIC 可以开始使用。
DMA(直接内存访问)获取描述符。
数据包到达 NIC。
DMA 将数据包写入 RX 环形缓冲区。
NIC 通过硬件中断(IRQ)告知驱动程序,进而告知 CPU 有新的流量准备处理。
在第一次硬件中断后,中断处理程序将其屏蔽,然后驱动程序使用软件中断(SoftIRQ),这对 CPU 的消耗要小得多(硬件中断不能被中断,这对 CPU 来说非常耗费资源)。
SoftIRQ 触发 NAPI 子系统(唤醒),该子系统调用 NIC 驱动程序的轮询函数。
CPU 处理传入的数据包。
在 SoftIRQ 的预算耗尽后,NAPI 系统重新进入休眠状态。如果 SoftIRQ 的预算用完,CPU 会继续执行下一个任务,/proc/net/softnet_stats 中的 time_squeezed 计数器会增加 1。
在 NIC 初始化时,驱动程序执行以下操作:
在内存(DMA 空间)中分配 Rx 和 Tx 队列。
启用 NAPI,默认情况下是关闭的。
注册中断处理程序。
启用硬件中断。
我建议您查看这个链接(),以获取流程的详细描述。
关键词
文件描述符:是操作系统用来识别打开文件的编号。
TSS(Tuple Space Search):这是 OVS 用于第二级表(dpcls)的技术,基本上依赖于对现有元组的哈希匹配。也就是说,当你能够在每个数据包中匹配相同值的元组时,你会创建一个哈希值来匹配它,并根据该哈希值进行转发,而不是逐个查找每个值。
命令总结
描述命令
显示中断计数器(IRQs 和 SoftIRQs)
$ cat /proc/interrupts
显示 SoftIRQ 进程
$ ps aux | grep ksoftirqd
监控每个 CPU 的 RX 缓冲区(接收 RX 和发送 TX)
$ watch -n1 grep RX /proc/softirqs
$ watch -n1 grep TX /proc/softirqs
查看由 NIC 丢弃的数据包数量
$ cat /sys/class/net/ens192/statistics/rx_missed_errors
References
Linux Foundation Wiki – sk_buff Kernel Flow
Linux Kernel Archive
SK Buffer – deep look
Linux Foundation Explanation of the SK_Buff
DevConf CZ 2018 – Linux Packet Flow (Video)
Linux Conf 2017 – Kernel-bypass networking for fun and profit (Video)
NIC Offloading – Master Thesis (Ondrej Hlavaty) (PDF)
Kernel-bypass techniques for high-speed network packet processing (PDF)
Kernel-bypass techniques for high-speed network packet processing (Video)
CloudFlare – Kernel bypass
Intel – OVS-DPDK Datapath Classifier – (Very Good for understanding how DPDK exactly works with Intel HW)
Intel – OVS DPDK Architectural Deep Dive (3 Part Video)
Intel – DPDK Open vSwitch: Accelerating the Path to the Guest (4 Part Video)
Inter Process Communication (3 part Article)
TCP/IP ARCHITECTURE, DESIGN, AND IMPLEMENTATION IN LINUX – Sameer Seth , M. Ajaykumar Venkatesulu (Book)
NAPI (NewAPI) – Network Driver
Illustrated Guide to Monitoring and Tuning the Linux Networking Stack: Receiving Data
Red Hat Enterprise Linux Network Performance Tuning Guide
Stack Overflow – Ring Buffers and DMA Memory – illustration of the process
How SKBs work []:
Src
———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: qs62318888
主题授权提示:请在后台主题设置-主题授权-激活主题的正版授权,授权购买:RiTheme官网