linux内核报文接收流程

网卡驱动接收报文netif_rx

在这里插入图片描述
netif_rx函数负责将设备驱动程序传递的数据包提交给网络代码进行处理,其中NET_RX_SUCCESS:表示数据包成功提交给上层处理,并未发生拥塞或被丢弃。
NET_RX_DROP:表示数据包在处理过程中被丢弃。
该函数的核心函数为netif_rx_internal->enqueue_to_backlog
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在enqueue_to_backlog函数中如果网卡设备没有运行则直接goto drop;丢弃该报文skb。如果sd->input_pkt_queue链表中元素的数量小于netdev_max_backlog,且skb_flow_limit函数中网络数据包的流量控制返回false(正常情况下不会进行流量限制)。则将该报文skb通过__skb_queue_tail(&sd->input_pkt_queue, skb);函数加入到input_pkt_queue链表中。

接收报文软中断处理net_rx_action

在这里插入图片描述
在这里插入图片描述
在net_dev_init函数中首先会将所有cpu的poll函数设置为process_backlog即sd->backlog.poll = process_backlog;然后设置NET_RX_SOFTIRQ类型的软中断处理函数为net_rx_action即接收报文的处理函数。

在这里插入图片描述
在这里插入图片描述
同时通过cpuhp_setup_state_nocalls函数注册dev_cpu_dead处理函数,可以在某个CPU离线(offline)时通过其他cpu处理相关的网络数据包队列和软中断。
在这里插入图片描述
在net_rx_action函数中处理流程如下:
1.获取当前CPU的软中断数据结构体指针sd,该结构体是通过this_cpu_ptr宏获取的,指向当前CPU的特定软中断数据。
2.根据配置的时间限制和预算,计算接收处理的时间限制和预算。
3.创建两个链表list和repoll,用于存储待处理的网络数据包和需要重新轮询的网络设备。
4.关闭本地中断,以确保接下来的操作在一个原子上下文中执行。
5.将当前CPU的poll_list链表中的网络数据包节点移动到list链表中,使用list_splice_init函数进行链表连接并初始化poll_list链表为空。
6.启用本地中断。
7.进入无限循环,直到处理完所有的网络数据包或满足退出条件:
(1)检查list链表是否为空,如果为空且没有待处理的网络设备需要重新轮询,则跳转到标签out处执行后续操作。
(2)从list链表中取出第一个网络数据包的napi_struct结构体指针n。
(3)使用napi_poll函数处理该网络数据包,并返回处理结果。
(4)检查预算是否耗尽或超过时间限制,如果是,则增加时间挤压计数time_squeeze,并break跳 出循环。
8.关闭本地中断。
9.将poll_list链表中剩余的网络数据包节点移动到list链表的末尾,并进行链表的初始化操作。
10.将repoll链表中的节点加入list链表的末尾。
11.将list链表中的节点重新连接到poll_list链表的末尾。
12.如果poll_list链表不为空,则通过__raise_softirq_irqoff函数触发NET_RX_SOFTIRQ软中断。
13.执行网络接收侧的RPS(Receive Packet Steering)相关操作,并启用本地中断。
14.执行__kfree_skb_flush函数,清空当前CPU的网络数据包缓存。

在这里插入图片描述
在这里插入图片描述
在napi_poll函数中主要是通过work = n->poll(n, weight);回调函数进一步将报文上送给网络协议栈处理的。其中weight表示NAPI的预算,即每次轮询操作可以处理的最大工作数量。NAPI轮询函数将执行n->poll()函数,该函数负责处理收到的网络数据包。如果轮询操作处理的工作数量超过了预算(work > weight),则会输出错误日志。并最后将此struct napi_struct加入到repoll链表中即下一次软中断时再次进入该napi_poll函数。

在这里插入图片描述
在这里插入图片描述
上文说到sd->backlog.poll注册的poll函数为process_backlog。在该函数中会通过 skb_queue_splice_tail_init(&sd->input_pkt_queue,&sd->process_queue);函数将sd->input_pkt_queue链表中的报文移动到sd->process_queue链表中。并在while ((skb = __skb_dequeue(&sd->process_queue)))循环中通过__netif_receive_skb函数上送到网络协议栈处理该报文。

网络协议栈入口__netif_receive_skb

在这里插入图片描述
网络协议栈的入口函数为__netif_receive_skb->__netif_receive_skb_one_core->__netif_receive_skb_core
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在__netif_receive_skb_core函数中会调用deliver_ptype_list_skb轮询所有与skb报文协议类型的struct packet_type的回调函数func。其中包括常见的arp协议和ip协议。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述arp协议栈static struct packet_type arp_packet_type通过dev_add_pack函数添加到ptype_base全局链表中。
在这里插入图片描述
在这里插入图片描述
ip协议栈也与上述方法一样。

在这里插入图片描述
可以看到在ip_rcv函数中首先调用ip_rcv_core对skb报文进行ip相关的检查,随后即进行iptables的NF_INET_PRE_ROUTING钩子点进行报文过滤,如何再通过ip_rcv_finish函数进一步将报文送至网络层和传输层。

总结

1.当网络设备接收到数据包时,硬件会将数据包存储在设备驱动的接收环(receive ring)中。
接着,硬件会触发中断,通知操作系统有数据包可用。
2.在接收到数据包后,驱动程序会调用netif_rx()函数来处理数据包。netif_rx()函数会将数据包放入软中断队列中,并触发软中断处理函数来处理数据包。
3.软中断处理函数net_rx_action通过poll回调函数process_backlog获取到接收到的数据包。并转交给__netif_receive_skb函数处理。
4.__netif_receive_skb_core()函数是网络协议栈中用于处理接收到的数据包的核心函数之一。它负责对数据包进行一系列的处理和分发。在软中断处理函数中,会调用__netif_receive_skb_core()函数来处理接收到的数据包。__netif_receive_skb_core()函数会对数据包进行预处理、更新统计信息,并将数据包传递给协议栈中的相应协议进行进一步处理。

当网络设备接收到数据包时,硬件将数据包存储在接收环中并触发中断,驱动程序通过netif_rx()函数处理数据包并放入软中断队列,软中断处理函数获取数据包后调用__netif_receive_skb_core()函数进行处理和分发。

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值