记录一下linux数据包从网卡进入协议栈的过程,不涉及驱动,不涉及其他层的协议处理。

内核是如何知道网卡收到数据的,这就涉及到网卡和内核的交互方式:

轮询(poll):内核周期性的检查网卡,查看是否收到数据。优点:数据包非常多的时候,这种处理方法会非常快速有效。缺点:数据包少的时候会CPU总是轮询却没有收到数据包,造成CPU资源的浪费。这种方法很少使用。

中断(interrupt):网卡收到数据就给内核发送硬件中断打断内核的正常运行,让内核来处理数据包。优点:在数据包少的时候CPU能及时中断其他任务来处理数据包,比较高效。缺点:数据包多的时候每个数据包都引发一次中断,造成CPU频繁地在收包过程和其他过程之间切换,浪费时间。在极端情况下收包中断可能会一直抢占CPU造成软中断无法运行,收包队列得不到处理,进而造成大量丢包。这就是所谓的receive-livelock。

Llinux早期是采用中断的方式处理数据包的,之后引入了另一种方式NAPI,NAPI结合了轮询和中断的优点,在数据包少的时候采用中断方式,数据包多的时候采用轮询的方式,从而在两种极端情况下也会有比较好的表现。

在NAPI下收包的过程

先看一个比较关键的结构softnet_data,每个逻辑CPU都有一个softnet_data结构,这个结构的poll_list是非常重要的。

struct softnet_data
{
struct net_device *output_queue;
//收报队列,这个队列是给传统收报方法兼容新收报架构用的(backlog_dev),用来模拟NAPI的
struct sk_buff_head input_pkt_queue;
//用于收包的net_device
struct list_head poll_list;
struct sk_buff *completion_queue; //backlog_dev是一个伪造的net_device用来来处理input_pkt_queue里的数据
struct net_device backlog_dev; /* Sorry. 8) */
};

收包过程可以分成两步:

  1. 当网卡收到数据包中断发生,中断处理程序就会把当前网卡的net_device插入当前CPU的softnet_data的poll_list链表,调度软中断。
  2. 软中断处理链表poll_list,读出数据包,放入协议栈。

第一步的中断的代码可以参考drivers/net/tg3.c文件的tg3_interrupt函数,中断发生的时候它会调用netif_rx_schedule把当前网卡的net_device插入当前CPU的softnet_data的poll_list链表。netif_rx_schedule函数又调度了软中断NET_RX_SOFTIRQ。大致结构就是下图这样子

第二步软中断在合适的时机得以执行,看一下他的执行过程:

static void net_rx_action(struct softirq_action *h)
{
struct softnet_data *queue = &__get_cpu_var(softnet_data);
unsigned long start_time = jiffies;
//预算,所有网卡的总配额
int budget = netdev_budget;
void *have; local_irq_disable(); while (!list_empty(&queue->poll_list)) {
struct net_device *dev; //预算用完了,或者时间太长了,跳出等下一轮处理
if (budget <= 0 || jiffies - start_time > 1)
goto softnet_break; local_irq_enable(); dev = list_entry(queue->poll_list.next,
struct net_device, poll_list);
have = netpoll_poll_lock(dev); if (dev->quota <= 0 || dev->poll(dev, &budget)) {
netpoll_poll_unlock(have);
local_irq_disable();
//没处理完,放到队尾准备下次处理,注意是list_move_tai,不是
//list_insert_tail
list_move_tail(&dev->poll_list, &queue->poll_list);
if (dev->quota < 0)
dev->quota += dev->weight;
else
dev->quota = dev->weight;
} else {
netpoll_poll_unlock(have);
dev_put(dev);
local_irq_disable();
}
}
//省略部分代码
}

软中断不能长时间占用CPU,否则会造成用户态进程长时间得不到调度,net_rx_action也一样。所以net_rx_action函数每次执行最多会处理budget个数据包(所有网卡都算),同时这budget个数据包也需要平均分配,不能只处理一个网卡造成其他网卡得不到处理,net_device的weight和quota是用来处理这个问题的。这个代码的大概意思是每次从poll_list里取出一个网卡,调用该网卡的poll函数尽可能多的收包(但是不会超过weight),poll函数收包后调用netif_receive_skb把数据包放入协议栈。如果网卡里的数据包没处理完就会把net_device继续放到poll_list链表等待下一次软中断继续处理,如果网卡里的数据包处理完了就把该net_device从poll_list摘除。

传统中断收包方式

linux网卡驱动还有部分是用的传统中断收包方式,为了兼容也都挪到了NAPI架构上。用softnet_data结构的backlog_dev伪造了一个net_device。中断发生的时候把数据包放到了softnet_data结构的input_pkt_queue链表里。

static int __init net_dev_init(void)
{
//省略部分代码
for_each_possible_cpu(i) {
struct softnet_data *queue; queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
queue->backlog_dev.weight = weight_p;
queue->backlog_dev.poll = process_backlog;
atomic_set(&queue->backlog_dev.refcnt, 1);
}
}

软中断的处理过程和NAPI类似。

linux网络收包过程的更多相关文章

  1. 代码学习-Linux内核网卡收包过程(NAPI)【转】

    转自:https://blog.csdn.net/crazycoder8848/article/details/46333761 版权声明:本文没有任何版权限制,任何人可以以任何方式使用本文. htt ...

  2. [转]Linux网络 - 数据包的发送过程

    转, 原文:https://segmentfault.com/a/1190000008926093 -------------------------------------------------- ...

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

    https://mp.weixin.qq.com/s/boRWlx1R7TX0NLuI2sZBfQ 作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供 ...

  4. Linux网络 - 数据包的接收过程【转】

    转自:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后 ...

  5. [转]Linux网络 - 数据包的接收过程

    转, 原文: https://segmentfault.com/a/1190000008836467 ------------------------------------------------- ...

  6. Linux网络 - 数据包的接收过程(转)

    https://segmentfault.com/a/1190000008836467

  7. Linux内核收包过程

    net/core/dev.c int __init net_dev_init(void) { queue->backlog.poll = process_backlog; open_softir ...

  8. Linux内核中网络数据包的接收-第一部分 概念和框架

    与网络数据包的发送不同,网络收包是异步的的.由于你不确定谁会在什么时候突然发一个网络包给你.因此这个网络收包逻辑事实上包括两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协 ...

  9. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

随机推荐

  1. 详解JVM中的内存模型是什么?

    强烈推荐 不管是找工作还是提升水平,都建议读一下<深入理解Java虚拟机>这本书,详细讲解了JVM中的内存管理.类加载过程.垃圾回收以及最重要的性能调优实战. 本博客也是参考了这本书,有不 ...

  2. charles安装破解步骤

    访问该地址https://www.zzzmode.com/mytools/charles/ 安装——生成破解工具charles.jar——替换

  3. iOS面试关于http知识点basic-01-http

    URL URL 的全称是 Uniform Resource Locator(统一资源定位符) 通过 1 个 URL,能找到互联网上唯一的 1 个资源 URL就是资源的地址.位置,互联网上的每个资源都有 ...

  4. 解压gzip格式文件(包括网页)

    先上源码 参数说名: - source :gzip格式流内容. - len: gzip流长度 - dest: 解压后字符流指针 - gzip: 压缩标志,非0时解压gzip格式,否则按照zip解压 说 ...

  5. [LeetCode]Mysql系列5

    题目1 1112. 每位学生的最高成绩 编写一个 SQL 查询,查询每位学生获得的最高成绩和它所对应的科目,若科目成绩并列,取 course_id 最小的一门.查询结果需按 student_id 增序 ...

  6. 硬核看房利器——Web 全景的实现

    作者:凹凸曼 - EC 疫情期间,打破社交距离限制的交互模式被推向前台,为不少行业的传统交易提供了想象的空间. 疫情时期,房地产租售业受到的冲击无疑是巨大的,由于人口流动的限制,需求量大幅减少,无法现 ...

  7. docker 搭建私有云仓库

    docker搭建私有仓库   registry私有仓库 下载docker-distribution软件包 yum install epel-release yum install docker-dis ...

  8. bash运行脚本的几种方式

    转载自https://www.jianshu.com/p/ba6efda13e23 转载地址:http://www.jquerycn.cn/a_8354 bash shell 脚本执行的方法有多种,本 ...

  9. RabbitMQ安装、集群搭建、概念解析

    RabbitMQ安装.集群搭建.概念解析 基本概念 为什么会产生MQ 1.解耦:采用异步方式实现业务需求达到解耦的目的. 2.缓冲流量,削峰填谷: 问:为什么会有流量冲击? 答:采用"直接调 ...

  10. Spring学习(八)AOP详解

    文章更新时间:2020/04/06 一.一个例子 在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让 ...