目录

硬中断处理

软中断处理


数据通过网络发送过来

硬中断处理

  1. 数据帧首先到达网卡的接收队列,分配RingBuffer
  2. DMA把数据搬运到网卡关联的内存
  3. 网卡向CPU发起硬中断,通知CPU有数据
  4. 调用驱动注册的硬中断处理函数
  5. 启动NAPI,触发软中断

上一分析说到网卡硬中断注册的函数igb_msix_ring

static irqreturn_t igb_msix_ring(int irq, void *data)
{struct igb_q_vector *q_vector = data;/* Write the ITR value calculated from the previous interrupt. */igb_write_itr(q_vector);napi_schedule(&q_vector->napi);return IRQ_HANDLED;
}

igb_write_itr仅记录硬件中断频率

static inline void ____napi_schedule(struct softnet_data *sd,struct napi_struct *napi)
{list_add_tail(&napi->poll_list, &sd->poll_list);//触发一个软中断NET_RX_SOFTIRQ__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
  • list_add_tail修改了napi的poll_list(双向链表,数据帧等着被处理),
  • 触发一个软中断NET_RX_SOFTIRQ
  • 网络包硬中断的工作到此结束。

软中断处理

判断softirq_pending标志

static int ksoftirqd_should_run(unsigned int cpu)
{return local_softirq_pending();
}

执行run_ksoftirqd->__do_softirq

asmlinkage __visible void __softirq_entry __do_softirq(void)
{while ((softirq_bit = ffs(pending))) {trace_softirq_entry(vec_nr);h->action(h);trace_softirq_exit(vec_nr);wakeup_softirqd();}
...
}

调用action中断函数

static __latent_entropy void net_rx_action(struct softirq_action *h)
{struct softnet_data *sd = this_cpu_ptr(&softnet_data);unsigned long time_limit = jiffies +usecs_to_jiffies(netdev_budget_usecs);int budget = netdev_budget;for (;;) {struct napi_struct *n;n = list_first_entry(&list, struct napi_struct, poll_list);//变量sd,调用poll函数budget -= napi_poll(n, &repoll);//budget 与 time_limit控制退出if (unlikely(budget <= 0 ||time_after_eq(jiffies, time_limit))) {sd->time_squeeze++;break;}}
核⼼逻辑是获取到当前 CPU变量 softnet_data,对其 poll_list 进⾏遍历, 然后执⾏到⽹卡驱动注册到的 poll 函数。
static int igb_poll(struct napi_struct *napi, int budget)
{if (q_vector->tx.ring)clean_complete = igb_clean_tx_irq(q_vector, budget);if (q_vector->rx.ring) {int cleaned = igb_clean_rx_irq(q_vector, budget);}}static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
{while (likely(total_packets < budget)) {union e1000_adv_rx_desc *rx_desc;struct igb_rx_buffer *rx_buffer;unsigned int size;rx_buffer = igb_get_rx_buffer(rx_ring, size);igb_put_rx_buffer(rx_ring, rx_buffer);cleaned_count++;/* fetch next buffer in frame if non-eop */if (igb_is_non_eop(rx_ring, rx_desc))continue;/* verify the packet layout is correct */if (igb_cleanup_headers(rx_ring, rx_desc, skb)) {skb = NULL;continue;}/* probably a little skewed due to removing CRC */total_bytes += skb->len;/* populate checksum, timestamp, VLAN, and protocol */igb_process_skb_fields(rx_ring, rx_desc, skb);napi_gro_receive(&q_vector->napi, skb);/* update budget accounting */total_packets++;}return total_packets;
}
  1. 从ringbuf中取出数据skb;
  2. 收取完数据以后,对其进⾏⼀些校验
  3. 设置 sbk 变量的 timestamp, VLAN id, protocol
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{skb_gro_reset_offset(skb);ret = napi_skb_finish(napi, skb, dev_gro_receive(napi, skb));trace_napi_gro_receive_exit(ret);}

napi_gro_receive函数代表的是⽹卡 GRO 特性,可以简单理解成把相关的⼩包合并成⼀个⼤包。

/* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
static void gro_normal_list(struct napi_struct *napi)
{if (!napi->rx_count)return;netif_receive_skb_list_internal(&napi->rx_list);INIT_LIST_HEAD(&napi->rx_list);napi->rx_count = 0;
}/* Queue one GRO_NORMAL SKB up for list processing. If batch size exceeded,* pass the whole batch up to the stack.*/
static void gro_normal_one(struct napi_struct *napi, struct sk_buff *skb)
{list_add_tail(&skb->list, &napi->rx_list);if (++napi->rx_count >= gro_normal_batch)gro_normal_list(napi);
}static gro_result_t napi_skb_finish(struct napi_struct *napi,struct sk_buff *skb,gro_result_t ret)
{switch (ret) {case GRO_NORMAL:gro_normal_one(napi, skb);break;...
}

最终调用 gro_normal_list将数据发送到网络协议栈。

参考

GitHub - yanfeizhang/coder-kung-fu: 开发内功修炼

linux内核网络收包过程—硬中断与软中断相关推荐

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

    本文通过学习RealTek8169/8168/8101网卡的驱动代码(drivers/net/r8169.c).梳理一下Linux下网卡的收包过程. 在下水平相当有限,有不当之处,还请大家斧正^_^ ...

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

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

  3. Linux内核网络数据包发送(一)

    Linux内核网络数据包发送(一) 1. 前言 2. 数据包发送宏观视角 3. 协议层注册 4. 通过 socket 发送网络数据 4.1 `sock_sendmsg`, `__sock_sendms ...

  4. 如何快速优化 Linux 内核 UDP 收包效率? | CSDN 博文精选

    作者 | dog250 责编 | 郭芮 出品 | CSDN 博客 现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈 ...

  5. Linux内核UDP收包为什么效率低?能做什么优化?

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK. 但是,即便Linux内核协议栈收包 ...

  6. Linux内核网络数据包发送(四)——Linux netdevice 子系统

    Linux内核网络数据包发送(四)--Linux netdevice 子系统 1. 前言 2. `dev_queue_xmit` and `__dev_queue_xmit` 2.1 `netdev_ ...

  7. Linux内核网络数据包发送(三)——IP协议层分析

    Linux内核网络数据包发送(三)--IP协议层分析 1. 前言 2. `ip_send_skb` 3. `ip_local_out` and `__ip_local_out` 3.1 netfilt ...

  8. Linux内核网络数据包发送(二)——UDP协议层分析

    Linux内核网络数据包发送(二)--UDP协议层分析 1. 前言 2. `udp_sendmsg` 2.1 UDP corking 2.2 获取目的 IP 地址和端口 2.3 Socket 发送:b ...

  9. linux网络收包过程

    记录一下linux数据包从网卡进入协议栈的过程,不涉及驱动,不涉及其他层的协议处理. 内核是如何知道网卡收到数据的,这就涉及到网卡和内核的交互方式: 轮询(poll):内核周期性的检查网卡,查看是否收 ...

最新文章

  1. spring elasticsearch 按条件删除_Elasticsearch系列之Query DSL
  2. Jquery高亮显示文本中重要的关键字
  3. 怎么修改linux用户名密码忘记,linux passwd命令设置或修改用户忘记密码
  4. node.js 下载安装及gitbook环境安装、搭建
  5. 北京大学 微软:预训练模型(Transformer)中的知识神经元
  6. C++学习成长的四个层次
  7. NLP产品级系统设计模式
  8. ORA-01017: invalid username/password; logon denied
  9. 天敏盒子系统停止服务器,天敏网络机顶盒今天怎么停服了?
  10. 一键GHOST v2019.08.12优盘教程
  11. 聊聊docker的使用心得
  12. 手把手学习Vue3.0:开发工具WebStorm和Vue模板文件介绍
  13. BS和CS架构的区别
  14. Emacs的日常生活
  15. 教小师妹学多线程,看完我写的例子,脸红成那样!
  16. Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
  17. 看看同一种字体是如何对应不同的字体文件的
  18. 一万八的M1 iPad Pro ,怎么就成了“期货”
  19. 项目管理心得--第一篇
  20. 软件工程——结构化分析方法

热门文章

  1. 考研复习时间安排初试篇
  2. 海明码,码距,海明校验码
  3. android 平板 吃鸡,吃鸡不卡的安卓平板
  4. JAVA实现生成原生二维码并上传至阿里云
  5. 华硕 PRIME Z490-PLUS+i7-10700K黑苹果EFI引导文件
  6. python 处理pdf文件 转成txt 批量提取pdf中的文字
  7. 安卓版的PanDownload 下载网盘资源
  8. Typora软件百度网盘下载地址
  9. 甲骨文一键修改root密码
  10. krpano限制场景视角