《Linux中断处理:上半部和下半部》

《Linux网络协议栈:中断下半部处理》

目录

数据包上送

网络中断下半部处理

总结

推荐阅读


在《Linux网络协议栈:网络包接收过程》中,我们介绍了网卡接收和发过数据在 Linux 内核中的处理过程,我们先来回顾一下网卡接收和发送数据的过程,如 图1 所示:

图1 网卡接收和发送数据过程

如上图所示,当网卡接收到从网络中发送过来的数据后,网卡会向 CPU 发起一个硬件中断。当 CPU 接收到网卡的硬件中断后,便会调用网卡驱动向内核注册的中断处理服务,如 NS8390网卡驱动 会向内核注册 ei_interrupt 中断服务。

由于在处理硬件中断服务时会关闭硬件中断,所以在处理硬件中断服务的过程中,如果发生了其他的硬件中断,也不能得到有效的处理,从而导致硬件中断丢失的情况。

为了避免这种情况出现,Linux 内核把中断处理分为:中断上半部中断下半部,上半部在关闭中断的情况下进行,而下半部在打开中断的情况下进行。

由于中断上半部在关闭中断的情况下进行,所以必须要快速完成,从而避免中断丢失的情况。而中断下半部处理是在打开中断的情况下进行的,所以可以慢慢进行。

一般来说,网卡驱动向内核注册的中断处理服务属于 中断上半部,如前面介绍的 NS8390网卡驱动 注册的 ei_interrupt 中断处理服务,而本文主要分析网卡 中断下半部 的处理。

数据包上送


在上一篇文章中,我们介绍过 ei_interrupt  中断处理服务首先会创建一个 sk_buff 数据包对象保存从网卡中接收到的数据,然后调用 netif_rx 函数将数据包上送给网络协议栈处理。

我们先来分析一下 netif_rx 函数的实现:

int netif_rx(struct sk_buff *skb)
{int this_cpu = smp_processor_id(); // 获取当前运行的CPUstruct softnet_data *queue;unsigned long flags;...queue = &softnet_data[this_cpu]; // 获取当前CPU的待处理的数据包队列local_irq_save(flags); // 关闭本地硬件中断// 如果待处理队列的数据包数量没超出限制if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {if (queue->input_pkt_queue.qlen) {...enqueue:dev_hold(skb->dev); // 增加网卡设备的引用计数器__skb_queue_tail(&queue->input_pkt_queue, skb); // 将数据包添加到待处理队列中__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);  // 启动网络中断下半部处理local_irq_restore(flags);return softnet_data[this_cpu].cng_level;}...goto enqueue;}...drop:local_irq_restore(flags); // 打开本地硬件中断kfree_skb(skb);           // 释放数据包对象return NET_RX_DROP;}

netif_rx 函数的参数就是要上送给网络协议栈的数据包,netif_rx 函数主要完成以下几个工作:

  • 获取当前 CPU 的待处理的数据包队列,在 Linux 内核初始化时,会为每个 CPU 创建一个待处理数据包队列,用于存放从网卡中读取到网络数据包。
  • 如果待处理队列的数据包数量没超出 netdev_max_backlog 设置的限制,那么调用 __skb_queue_tail 函数把数据包添加到待处理队列中,并且调用 __cpu_raise_softirq 函数启动网络中断下半部处理。
  • 如果待处理队列的数据包数量超出 netdev_max_backlog 设置的限制,那么就把数据包释放。

netif_rx 函数的处理过程如 图2 所示:

图2 netif_rx 函数的处理过程

所以,netif_rx 函数的主要工作就是把接收到的数据包添加到待处理队列中,并且启动网络中断下半部处理。

对于 Linux 内核的中断处理机制可以参考我们之前的文章《Linux中断处理:上半部和下半部》,这里就不详细介绍了。在本文中,我们只需要知道网络中断下半部处理例程为 net_rx_action 函数即可。

网络中断下半部处理


上面说了,网络中断下半部处理例程为 net_rx_action 函数,所以我们主要分析 net_rx_action 函数的实现:

static void net_rx_action(struct softirq_action *h)
{int this_cpu = smp_processor_id();                    // 当前运行的CPUstruct softnet_data *queue = &softnet_data[this_cpu]; // 当前CPU对于的待处理数据包队列...for (;;) {struct sk_buff *skb;local_irq_disable();skb = __skb_dequeue(&queue->input_pkt_queue); // 从待处理数据包队列中获取一个数据包local_irq_enable();if (skb == NULL)break;...{struct packet_type *ptype, *pt_prev;unsigned short type = skb->protocol; // 网络层协议类型pt_prev = NULL;...// 使用网络层协议处理接口处理数据包for (ptype = ptype_base[ntohs(type)&15]; ptype; ptype = ptype->next) {if (ptype->type == type&& (!ptype->dev || ptype->dev == skb->dev)){if (pt_prev) {atomic_inc(&skb->users);// 如处理IP协议数据包的 ip_rcv() 函数pt_prev->func(skb, skb->dev, pt_prev);}pt_prev = ptype;}}if (pt_prev) {// 如处理IP协议数据包的 ip_rcv() 函数pt_prev->func(skb, skb->dev, pt_prev);} elsekfree_skb(skb);}...}...return;}

net_rx_action 函数主要完成以下几个工作:

  • 从待处理数据包队列中获取一个数据包,如果数据包为空,那么就退出网络中断下半部。

    如果获取的数据包不为空,那么就从数据包的以太网头部中获取到网络层协议的类型。然后根据网络层协议类型从 ptype_base 数组中获取数据处理接口,再通过此数据处理接口来处理数据包。

    在内核初始化时,通过调用 dev_add_pack 函数向 ptype_base 数组中注册网络层协议处理接口,如 ip_init 函数:

static struct packet_type ip_packet_type = {__constant_htons(ETH_P_IP),NULL,ip_rcv,  // 处理IP协议数据包的接口(void*)1,NULL,};void __init ip_init(void)
{// 注册网络层协议处理接口dev_add_pack(&ip_packet_type);...}

所以,net_rx_action 函数主要从待处理队列中获取数据包,然后根据数据包的网络层协议类型,找到相应的处理接口处理数据包。其过程如 图3 所示:

从上图可知,net_rx_action 函数将数据包交由网络层协议处理接口后就不管了,而网络层协议处理接口接管数据包后,会对数据包进行进一步处理,如判断数据包的合法性(数据包是否损坏、数据包是否发送给本机)。如果数据包是合法的,就会交由传输层协议处理接口处理。

总结


本文主要介绍了网络中断下半部的处理,从分析可知,网络中断下半部主要工作是从待处理队列中获取数据包,并且根据数据包的网络层协议类型来找到相应的处理接口,然后把数据包交由网络层协议处理接口进行处理。

对于网络层协议处理接口的相关过程,我们将会在后面的文章继续分析。

推荐阅读


《Linux网络 - 数据包的接收过程》

《监视和调整Linux网络协议栈:接收数据》

《监控和调整Linux网络协议栈的图解指南:接收数据》

《监视和调整Linux网络协议栈:发送数据》

《深入理解 Cilium 的 eBPF(XDP)收发包路径:数据包在Linux网络协议栈中的路径》

《FD.io VPP:探究分段场景下vlib_buf在收发包的处理(dpdk_plugin.so)、rte_mbuf与vlib_buf 关系》

《DPDK 网卡收包流程》

《Linux网络协议栈:网卡收包分析》

《深入理解 Cilium 的 eBPF(XDP)收发包路径:数据包在Linux网络协议栈中的路径》

《iptables详解(1):iptables概念》

《iptables详解(2):路由表》

《eBPF.io eBPF文档:扩展的数据包过滤器(BPF)》

《Linux eBPF和XDP高速处理数据包;使用EBPF编写XDP网络过滤器;高性能ACL》

《深入理解 Cilium 的 eBPF 收发包路径》

《DPDK 网卡收包流程》

《ethtool 原理介绍和解决网卡丢包排查思路》

《Linux网络协议栈:网络包接收过程》

Linux网络协议栈:中断下半部处理相关推荐

  1. Linux网络协议栈:网络包接收过程

    目录 一 Linux网络收包总览 二 Linux启动 2.1 创建ksoftirqd内核线程 2.2 网络子系统初始化 2.3 协议栈注册 2.4 网卡驱动初始化 2.5 启动网卡 三 迎接数据的到来 ...

  2. Linux网络协议栈:网卡收包分析

    Table of Contents 网卡收包 一,框架 二,初始化 三,驱动收包 四,内核处理 参考文章 推荐阅读 网卡收包 内核网络模块如何初始化? 内核如何通过网卡驱动收发数据包? 驱动收到的数据 ...

  3. Linux网络协议栈:NAPI机制与处理流程分析(图解)

    Table of Contents NAPI机制 NAPI缺陷 使用 NAPI 先决条件 非NAPI帧的接收 netif_rx - 将网卡中收到的数据包放到系统中的接收队列中 enqueue_to_b ...

  4. linux 虚拟机大量udp请求失败_理解 Linux 网络栈:Linux 网络协议栈简单总结分析...

    1. Linux 网络路径 1.1 发送端 1.1.1 应用层 (1) Socket 应用层的各种网络应用程序基本上都是通过 Linux Socket 编程接口来和内核空间的网络协议栈通信的.Linu ...

  5. Linux网络协议栈:一个TCP链接的耗时

    <一次系统调用开销到底有多大?strace.time.perf命令> 目录 一 正常TCP连接建立过程 二 TCP连接建立时的异常情况 1)客户端connect系统调用耗时失控 2)半/全 ...

  6. 监视和调整Linux网络协议栈:发送数据

    目录 有关监视和调整Linux网络堆栈的一般建议 总览 详细外观 协议族注册 通过套接字发送网络数据 sock_sendmsg,__sock_sendmsg和__sock_sendmsg_nosec ...

  7. 监控和调整Linux网络协议栈的图解指南:接收数据

    Table of Contents 入门 最初设定 数据到达 网络数据处理开始 网络数据处理继续 协议栈和用户态套接字 结论 监视和调整Linux网络协议栈:接收数据(图解):https://rtoa ...

  8. 监视和调整Linux网络协议栈:接收数据

    Table of Contents 有关监视和调整Linux网络协议栈的建议 总览 详细外观 网络设备驱动程序 初始化 网络设备初始化 启动网络设备 监控网络设备 调整网络设备 SoftIRQ 什么是 ...

  9. 理解 Linux 网络栈:Linux 网络协议栈简单总结

    1. Linux 网络路径 1.1 发送端 1.1.1 应用层 (1) Socket 应用层的各种网络应用程序基本上都是通过 Linux Socket 编程接口来和内核空间的网络协议栈通信的.Linu ...

最新文章

  1. 学python最好的方式-Python 学习怎样开始比较好?
  2. 01 ORA系列:ORA-00904 标识符无效 invalid identifier
  3. java 控制台刷屏 dll_Java刷屏问题,下面是我编的代码,请大神帮忙解决下,谢谢...
  4. linux下程序如何实现单实例运行
  5. leetcode322. 零钱兑换
  6. source insight常用命令--实际使用中比较常用的
  7. eclipes创建一个web项目web.xml不能自动更新的原因(web.xml和@WebServlet的作用)
  8. 嵌入式系统——文件系统
  9. Json概述以及python对json的相关操作
  10. linux 如何看图软件,深度看图(linux看图软件) v1.2 官方最新版
  11. 分布式事务之TCC事务
  12. 领取敬业福或新春红包
  13. 手机内存不够用,蒲公英X1让U盘秒变私有云
  14. 电脑mac地址的查看的三种方式
  15. 女友老爸开了中介公司让我抽空搞开发个租房App,像贝壳一样就行.....
  16. 再逼自己一把,把项目做出来....
  17. windows10下激活conda环境报错CommandNotFoundError: Your shell has not been properly configured to use conda
  18. redis的GEO实战 (RedisTemplate)
  19. 刷题记录:牛客NC15036了断局
  20. PM 五大过程组、十大管理

热门文章

  1. mui ajax的值php怎样获取,Mui-ajax获取服务器请求
  2. JumpServer1.5.8堡垒机专题课--prometheus监控主机和MySQL
  3. Python3多进程与多线程区别及使用(1.进程)
  4. 前端知识点总结——VUE
  5. Android实例-屏幕操持常亮(XE8+小米2)
  6. Apple individual program 加入之后的玩法 官方资源
  7. (转)MySQL命令行--导入导出数据库
  8. mysql事务日志备份_事务日志备份 (SQL Server)
  9. 怎么抽象mysql数据库_一个用于mysql的数据库抽象层函数库
  10. node path html模块,深入理解node.js之path模块