数据包接收系列 — NAPI的原理和实现
本文主要内容:简单分析NAPI的原理和实现。
内核版本:2.6.37
概述
NAPI是linux新的网卡数据处理API,据说是由于找不到更好的名字,所以就叫NAPI(New API),在2.5之后引入。
简单来说,NAPI是综合中断方式与轮询方式的技术。
中断的好处是响应及时,如果数据量较小,则不会占用太多的CPU事件;缺点是数据量大时,会产生过多中断,
而每个中断都要消耗不少的CPU时间,从而导致效率反而不如轮询高。轮询方式与中断方式相反,它更适合处理
大量数据,因为每次轮询不需要消耗过多的CPU时间;缺点是即使只接收很少数据或不接收数据时,也要占用CPU
时间。
NAPI是两者的结合,数据量低时采用中断,数据量高时采用轮询。平时是中断方式,当有数据到达时,会触发中断
处理函数执行,中断处理函数关闭中断开始处理。如果此时有数据到达,则没必要再触发中断了,因为中断处理函
数中会轮询处理数据,直到没有新数据时才打开中断。
很明显,数据量很低与很高时,NAPI可以发挥中断与轮询方式的优点,性能较好。如果数据量不稳定,且说高不高
说低不低,则NAPI则会在两种方式切换上消耗不少时间,效率反而较低一些。
实现
来看下NAPI和非NAPI的区别:
(1) 支持NAPI的网卡驱动必须提供轮询方法poll()。
(2) 非NAPI的内核接口为netif_rx(),NAPI的内核接口为napi_schedule()。
(3) 非NAPI使用共享的CPU队列softnet_data->input_pkt_queue,NAPI使用设备内存(或者
设备驱动程序的接收环)。
(1) NAPI设备结构
- /* Structure for NAPI scheduling similar to tasklet but with weighting */
- struct napi_struct {
- /* The poll_list must only be managed by the entity which changes the
- * state of the NAPI_STATE_SCHED bit. This means whoever atomically
- * sets that bit can add this napi_struct to the per-cpu poll_list, and
- * whoever clears that bit can remove from the list right before clearing the bit.
- */
- struct list_head poll_list; /* 用于加入处于轮询状态的设备队列 */
- unsigned long state; /* 设备的状态 */
- int weight; /* 每次处理的最大数量,非NAPI默认为64 */
- int (*poll) (struct napi_struct *, int); /* 此设备的轮询方法,非NAPI为process_backlog() */
- #ifdef CONFIG_NETPOLL
- ...
- #endif
- unsigned int gro_count;
- struct net_device *dev;
- struct list_head dev_list;
- struct sk_buff *gro_list;
- struct sk_buff *skb;
- };
(2) 初始化
初始napi_struct实例。
- void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
- int (*poll) (struct napi_struct *, int), int weight)
- {
- INIT_LIST_HEAD(&napi->poll_list);
- napi->gro_count = 0;
- napi->gro_list = NULL;
- napi->skb = NULL;
- napi->poll = poll; /* 设备的poll函数 */
- napi->weight = weight; /* 设备每次poll能处理的数据包个数上限 */
- list_add(&napi->dev_list, &dev->napi_list); /* 加入设备的napi_list */
- napi->dev = dev; /* 所属设备 */
- #ifdef CONFIG_NETPOLL
- spin_lock_init(&napi->poll_lock);
- napi->poll_owner = -1;
- #endif
- set_bit(NAPI_STATE_SCHED, &napi->state); /* 设置NAPI标志位 */
- }
(3) 调度
在网卡驱动的中断处理函数中调用napi_schedule()来使用NAPI。
- /**
- * napi_schedule - schedule NAPI poll
- * @n: napi context
- * Schedule NAPI poll routine to be called if it is not already running.
- */
- static inline void napi_schedule(struct napi_struct *n)
- {
- /* 判断是否可以调度NAPI */
- if (napi_schedule_prep(n))
- __napi_schedule(n);
- }
判断NAPI是否可以调度。如果NAPI没有被禁止,且不存在已被调度的NAPI,
则允许调度NAPI,因为同一时刻只允许有一个NAPI poll instance。
- /**
- * napi_schedule_prep - check if napi can be scheduled
- * @n: napi context
- * Test if NAPI routine is already running, and if not mark it as running.
- * This is used as a condition variable insure only one NAPI poll instance runs.
- * We also make sure there is no pending NAPI disable.
- */
- static inline int napi_schedule_prep(struct napi_struct *n)
- {
- return !napi_disable_pending(n) && !test_and_set_bit(NAPI_STATE_SCHED, &n->state);
- }
- static inline int napi_disable_pending(struct napi_struct *n)
- {
- return test_bit(NAPI_STATE_DISABLE, &n->state);
- }
- enum {
- NAPI_STATE_SCHED, /* Poll is scheduled */
- NAPI_STATE_DISABLE, /* Disable pending */
- NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
- };
NAPI的调度函数。把设备的napi_struct实例添加到当前CPU的softnet_data的poll_list中,
以便于接下来进行轮询。然后设置NET_RX_SOFTIRQ标志位来触发软中断。
- void __napi_schedule(struct napi_struct *n)
- {
- unsigned long flags;
- local_irq_save(flags);
- ____napi_schedule(&__get_cpu_var(softnet_data), n);
- local_irq_restore(flags);
- }
- static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi)
- {
- /* 把napi_struct添加到softnet_data的poll_list中 */
- list_add_tail(&napi->poll_list, &sd->poll_list);
- __raise_softirq_irqoff(NET_RX_SOFTIRQ); /* 设置软中断标志位 */
- }
(4) 轮询方法
NAPI方式中的POLL方法由驱动程序提供,在通过netif_napi_add()加入napi_struct时指定。
在驱动的poll()中,从自身的队列中获取sk_buff后,如果网卡开启了GRO,则会调用
napi_gro_receive()处理skb,否则直接调用netif_receive_skb()。
POLL方法应该和process_backlog()大体一致,多了一些具体设备相关的部分。
(5) 非NAPI和NAPI处理流程对比
以下是非NAPI设备和NAPI设备的数据包接收流程对比图:
NAPI方式在上半部中sk_buff是存储在驱动自身的队列中的,软中断处理过程中驱动POLL方法调用
netif_receive_skb()直接处理skb并提交给上层。
- /**
- * netif_receive_skb - process receive buffer from network
- * @skb: buffer to process
- * netif_receive_skb() is the main receive data processing function.
- * It always succeeds. The buffer may be dropped during processing
- * for congestion control or by the protocol layers.
- * This function may only be called from softirq context and interrupts
- * should be enabled.
- * Return values (usually ignored):
- * NET_RX_SUCCESS: no congestion
- * NET_RX_DROP: packet was dropped
- */
- int netif_receive_skb(struct sk_buff *skb)
- {
- /* 记录接收时间到skb->tstamp */
- if (netdev_tstamp_prequeue)
- net_timestamp_check(skb);
- if (skb_defer_rx_timestamp(skb))
- return NET_RX_SUCCESS;
- #ifdef CONFIG_RPS
- ...
- #else
- return __netif_receive_skb(skb);
- #endif
- }
__netif_receive_skb()在上篇blog中已分析过了,接下来就是网络层来处理接收到的数据包了。
数据包接收系列 — NAPI的原理和实现相关推荐
- 数据包接收系列 — 数据包的接收过程(宏观整体)
本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细. 本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个 ...
- 数据包接收系列 — IP协议处理流程(一)
本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog IP报头 IP报头: struct iphdr { #if defi ...
- 【数据包捕获技术】libpcap原理
在当前高速网络环境中,如何实时.高效.完整.快速捕获数据包是能否准确分析网络数据的基础和网络安全防护系统的关键技术.目前已经有从硬件/软件/软硬结合多种解决方案被不断的提出,用于提升数据包转发与捕获的 ...
- 网卡驱动和队列层中的数据包接收
一.从网卡说起 这并非是一个网卡驱动分析的专门文档,只是对网卡处理数据包的流程进行一个重点的分析.这里以Intel的e100驱动为例进行分析. 大多数网卡都是一个PCI设备,PCI设备都包含了一个标准 ...
- UDP数据包接收逻辑的优化修改以及对性能的影响
UDP数据包接收逻辑的优化修改以及对性能的影响 #include <stdio.h> #include <stdlib.h> #include <unistd.h> ...
- Linux网络数据包接收处理过程
因为要对百万.千万.甚至是过亿的用户提供各种网络服务,所以在一线互联网企业里面试和晋升后端开发同学的其中一个重点要求就是要能支撑高并发,要理解性能开销,会进行性能优化.而很多时候,如果你对Linux底 ...
- linux tcp 包大小,Linux TCP数据包接收处理 --- 转
在接收流程一节中可以看到数据包在读取到用户空间前,都要经过tcp_v4_do_rcv处理,从而在receive queue中排队. 在该函数中,我们只分析当连接已经建立后的数据包处理流程,也即tcp_ ...
- DHCP的工作原理 公网IP与私网IP 网络通信的流程与数据包发送 网卡的工作原理 网卡丢包问题
网络通信的四个要素 本机的IP地址 子网掩码 网关的IP地址 DNS服务器的IP地址 获取以上配置,有两种方式 : 静态获取==>手动配置 动态获取==>dhcp自动获取 DHCP的作用 ...
- JAVA实现udp接收文件数据,java – 播放以UDP数据包接收的原始PCM音频
以下是获取输出线并在其上播放PCM的简单示例.在运行时,它会播放大约一秒长的恼人的哔哔声. import javax.sound.sampled.AudioFormat; import javax.s ...
- linux内核网络协议栈--数据包的接收过程(二十二)
与其说这篇文章分析了网卡驱动中中数据包的接收,还不如说基于Kernel:2.6.12,以e100为例,对网卡驱动编写的一个说明.当然,对数据包的接收说的很清楚. 一.从网卡说起 这并非是一个网卡驱动分 ...
最新文章
- Java 8 (8) 默认方法
- [云炬创业基础笔记]第五章创业机会评估测试8
- 计算机网络计算机应用答案,计算机网络与应用(含答案).doc
- Google Hangouts支持使用Firefox WebRTC
- gridview中的nbsp;问题解决方法
- 美丽又实用的欧拉螺线,数学界当之无愧的画家!
- mac升级php7,MAC更新自带php版本到7.0
- php post 400,post数据时报错:远程服务器返回错误: (400) 错误的请求。
- 对于Vue组件的初步认识(未整理)
- vue防抖和节流是什么_前端节流和防抖的区别
- 面向对象编程(七):接口(抽象类)
- SQL:pgsql查询geom参考系以及更改geom参考系
- matlab里的timer,关于Matlab中用timer来实现多线程机制
- f检验matlab计算,F检验(F-Test)计算公式与在线计算器_三贝计算网_23bei.com
- 模块一 day03 Python基础
- kali如何对网站进行ddos攻击
- UG NX 10 控制基准平面的显示大小
- 英语绕口令(转)[Blog synchronous]
- Iso中查看Windows版本
- 解答:为什么判断一个数是否为素数时只需开平方根就行了!