linux内核网络协议栈--网卡报文收发(十六)

版本说明

Linux版本: 3.10.103
网卡驱动: ixgbev

报文收发简单流程

网卡驱动默认采用的是NAPI的报文处理方式。即中断+轮询的方式,网卡收到一个报文之后会产生接收中断,并且屏蔽中断,直到收够了netdev_max_backlog个报文(默认300)或者收完网卡上的所有报文之后,重新打开中断。

网卡数据处理

网卡初始化

  • 内核启动时会调用do_initcalls,从而调用注册的初始化接口net_dev_init,net_dev_init注册软中断的回调函数,分别为接收和发送的:NET_RX_SOFTIRQ = net_rx_action,NET_TX_SOFTIRQ = net_tx_action。
  • 网卡驱动加载时调用ixgbe_init_module注册一个PCI驱动。
  • 接着调用probe对应的ixgbe_probe做一些准备工作如下:
    • 创建ixgbe_adapter数据结构,这是网卡的一个实例,其中包含了网卡的所有数据和接口(netdev)。
    • netdev注册了网卡的所有操作:ixgbe_netdev_ops和网卡的features。
    • ixgbe_init_interrupt_scheme主要是设置网卡的NAPI对应的poll接口,这里主要是ixgbe_poll。
  • 网卡激活时会调用netdev上的open函数ixgbe_open,完成工作如下:
    • 设置接收和发送队列,通过DMA讲PCI网卡地址和队列建立映射。
    • 给网卡注册硬中断:ixgbe_open–>ixgbe_request_irq–>ixgbe_request_msix_irqs注册中断回调ixgbe_msix_clean_rings。(MSIX中断时)
  • ixgbe_close函数所做的事情:
    • 关闭中断并且释放中断向量号。
    • 释放申请的相应空间。

网卡收发数据

  • 当网卡接收到数据帧时,DMA将数据搬运到对应的rx_ring,然后产生一个硬中断,调用接口ixgbe_msix_clean_rings。
  • 硬中断回调函数ixgbe_msix_clean_rings会调用当前CPU对应的NET_RX_SOFTIRQ,即net_rx_action。其中的主要实现NAPI操作如下:
    • 关闭硬中断。
    • 调用该CPU的poll_list中的所有poll函数,即ixgbe_poll。
    • 开启硬中断。
  • 轮询函数ixgbe_poll主要是处理接收发送任务:
    • 循环处理每个ring中的数据,ixgbe_clean_tx_irq,此处发送tx_work_limit个报文(128)或者没有报文后完成,并且调用netif_wake_subqueue触发发送软中断NET_TX_SOFTIRQ,即net_tx_action。
    • 循环处理每个ring中的数据,ixgbe_clean_rx_irq,此处接收weight个报文(64),总共收取netdev_budget(300)个,并且调用ixgbe_rx_skb进行下一步的处理。
  • ixgbe_rx_skb最终会处理过GRO之后调用到netif_receive_skb,此处忽略rps。
  • netif_receive_skb最终调用__netif_receivve_skb_core进入协议栈处理各种协议。
    • 首先根据下面介绍的rx_handler类型调用ovs或者桥处理函数进入L2的处理。
    • 然后根据pttype_base处理相关的L3协议处理。

备注

  • ixgbe拥有64个rx_ring,每个ring默认有512个buffer,最小64,最大4096。
  • 由以上可知网卡默认可以缓存32768个报文,当网卡接收报文的速度过快,接收软中断无法处理的时候,系统进入扼流(throttle)状态,所有后续的报文都被丢弃,直到rx_ring有空间了。
  • 通过DMA地址映射的方式,网卡接收数据相当于零拷贝。

TUN虚拟网卡

  • 网卡驱动加载时调用tun_init注册一个MISC驱动,生成/dev/net/tun的设备文件。
  • 接着调用open对应的tun_chr_open做主要创建socket。
  • 随后调用ioctl对应的tun_chr_ioctl,完成一些需要的配置和信息获取,包括校验、连接状态、offload、mac等。
  • 通过调用write发送报文,主要是调用tun_chr_aio_write生成skb格式报文,最终通过调用netif_rx进入协议栈,通过三层转发到对应的端口发送。
  • 通过调用read接收报文,主要是调用tun_chr_aio_read,从内核对应的sock队列上拿到报文,然后将数据上传到用户层。

注:
因为网卡接收报文后最终经过四层的时候,会将报文根据协议几元组找到对应的sock结构,放入队列。上层调用read或者recv的时候都是从socket对应的sock结构队列上取数据。

tun虚拟网卡流程图:
在这里插入图片描述

VETH虚拟网卡

  • 网卡驱动加载时调用veth_init注册一个netlink驱动。
  • 接着调用newlink对应的veth_newlink,创建两个网络设备,两者互为对方的peer。
  • 发送报文时会调用ndo_start_xmit对应的veth_xmit,实际操作为将skb的dev设置为自己的peer,调用到netif_rx接收到协议栈。

veth虚拟网卡示意图:
在这里插入图片描述

QEMU虚拟机网络通信

  • 主机vhost驱动加载时调用vhost_net_init注册一个MISC驱动,生成/dev/vhost-net的设备文件。

  • 主机qemu-kvm启动时调用open对应的vhost_net_open做主要创建队列和收发函数的挂载,接着调用ioctl启动内核线程vhost,做收发包的处理。

  • 主机qemu通过ioctl配置kvm模块,主要设置通信方式,因为主机vhost和virtio只进行报文的传输,kvm进行提醒。

  • 虚拟机virtio模块注册,生成虚拟机的网络设备,配置中断和NAPI。

  • 虚拟机发包流程如下:

    • 直接从应用层走协议栈最后调用发送接口ndo_start_xmit对应的start_xmit,将报文放入发送队列,vp_notify通知kvm。
    • kvm通过vmx_handle_exit一系列调用到wake_up_process唤醒vhost线程。
    • vhost模块的线程激活并且拿到报文,在通过之前绑定的发送接口handle_tx_kick进行发送,调用虚拟网卡的tun_sendmsg最终到netif_rx接口进入主机内核协议栈。
  • 虚拟机收包流程如下:

    • tap设备的ndo_start_xmit对应的tun_net_xmit最终调用到wake_up_process激活vhost线程,调用handle_rx_kick,将报文放入接收队列。
    • 通过一系列的调用到kvm模块的接口kvm_vcpu_kick,向qemu虚拟机注入中断。
    • 虚拟机virtio模块中断调用接口vp_interrupt,调用virtnet_poll,再调用到netif_receive_skb进入虚拟机的协议栈。

qemu虚拟机通信流程图:
在这里插入图片描述

Offload技术

发送数据

  • TSO(TCP Segmentation Offload)使得网络协议栈能够将大块buffer推送至网卡,然后网卡执行分片工作,这样减轻了CPU的负荷,但TSO需要硬件来实现分片功能,也叫LSO。
  • UFO(UDP Fragmentation Offload)类似。
  • GSO(Generic Segmentation Offload),它比TSO更通用,基本思想就是尽可能的推迟数据分片直至发送到网卡驱动之前,此时会检查网卡是否支持分片功能(如TSO、UFO),如果支持直接发送到网卡,如果不支持就进行回调分片后再发往网卡。这样大数据包只需走一次协议栈,而不是被分割成几个数据包分别走,这就提高了效率。

接收数据

  • LRO(Large Receive Offload)通过将接收到的多个TCP数据聚合成一个大的数据包,然后传递给网络协议栈处理,以减少上层协议栈处理开销,提高系统接收TCP数据包的能力。
  • GRO(Generic Receive Offloading),LRO使用发送方和目的地IP地址,IP封包ID,L4协议三者来区分段,对于从同一个SNAT域的两个机器发向同一目的IP的两个封包可能会存在相同的IP封包ID(因为不是全局分配的ID),这样会有所谓的卷绕的bug。GRO采用发送方和目的地IP地址,源/目的端口,L4协议三者来区分作为改进。所以对于后续的驱动都应该使用GRO的接口,而不是LRO。另外,GRO也支持多协议。

原文链接:https://zhaozhanxu.com/2016/07/12/Linux/2016-07-12-Linux-Kernel-Pkts_Processing1/

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核网络协议栈是指在操作系统内核中实现的一系列网络协议和功能。它负责处理网络数据包的收发、路由选择、协议解析等一系列操作,以实现网络通信和数据传输。 Linux内核网络协议栈包括以下几个层次: 1. 网络层(Network Interface Layer):负责处理物理网络的驱动程序和硬件设备的通信。它提供了对底层网络设备的抽象,如以太网、Wi-Fi、蓝牙等。 2. 网络层(Network Layer):负责处理IP协议相关的操作,包括IP地址分配、路由选择和IP数据包的转发等。其中主要的协议有IPv4和IPv6。 3. 传输层(Transport Layer):负责处理端到端的数据传输,主要通过TCP(传输控制协议)和UDP(用户数据报协议)来实现。TCP提供可靠的、面向连接的数据传输,而UDP提供无连接的、不可靠的数据传输。 4. 应用层(Application Layer):负责处理特定应用程序的数据传输和协议,如HTTP、FTP、DNS等。应用层协议依赖于传输层和网络层的支持,通过这些协议实现应用程序之间的通信。 此外,Linux内核还提供了一些额外的功能,如网络地址转换(NAT)、防火墙(iptables)、网络隧道(Tunneling)等,以满足网络通信和安全的需求。 总之,Linux内核网络协议栈是一个复杂而庞大的系统,通过不同层次的协议和功能实现了网络通信的各个方面。它为应用程序提供了丰富的网络功能,使得Linux成为一个强大的网络操作系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值