数据包接收系列 — NAPI的原理和实现
本文主要内容:简单分析NAPI的原理和实现。
内核版本:2.6.37
Author:zhangskd @ csdn
概述
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的原理和实现的更多相关文章
- 数据包接收系列 — IP协议处理流程(二)
本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog 我们接着来看数据包如何发往本地的四层协议. ip_local_del ...
- 数据包接收系列 — IP协议处理流程(一)
本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog IP报头 IP报头: struct iphdr { #if defi ...
- Linux内核二层数据包接收流程
本文主要讲解了Linux内核二层数据包接收流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式从内核高层面上梳理了二层数据包接收的流程,希望可以对大家有所帮助.阅 ...
- linux 内核网络数据包接收流程
转:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后面 ...
- 在dubbo的一端,看Netty处理数据包,揭网络传输原理
如今,我们想要开发一个网络应用,那是相当地方便.不过就是引入一个框架,然后设置些参数,然后写写业务代码就搞定了. 写业务代码自然很重要,但是你知道: 你的数据是怎么来的吗?通过网络传输过来的呗. 你知 ...
- IP数据包格式与ARP转发原理
一.网络层简介1.网络层功能2.网络层协议字段二.ICMP与封装三.ARP协议与ARP欺骗1.ARP协议2.ARP欺骗 1.网络层功能 1. 定义了基于IP地址的逻辑地址2. 连接不同的媒介3. 选择 ...
- “ping”命令的原理就是向对方主机发送UDP数据包,HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”
Socket 是一套建立在TCP/IP协议上的接口不是一个协议 应用层: HTTP FTP SMTP Web 传输层: 在两个应用程序之间提供了逻辑而不是物理的通信(TCP UDP) T ...
- 多CPU下基于e1000e驱动的数据包以及网卡中断流程分析.doc
http://wenku.baidu.com/link?url=mMKDH_fKmUXN7L6rANIFHjoHdKCYBLlDrqoYB1daDTEkNFk9Bt9xlJtS_4BKBj6w22WD ...
- IP报文解析及基于IP 数据包的洪水攻击
版本(4bit) 报头长度(4bit) 优先级和服务类型(8bit) 总长度(16bit) 标识(16bit) 标志(3bit) 分段偏移(13bit) 存活期(8bit) 协议(8bit) 报头校验 ...
随机推荐
- 详解EBS接口开发之供应商导入
(一)供应商常用标准表简介 1.1 常用标准表 如下表中列出了与供应商相关的表和说明: 表名 说明 其他信息 ap_suppliers 供应商头表 供应商的头信息如:供应商名.供应商编码.税号等 ...
- React Native组件只Image
不管在Android还是在ios原生的开发中,图片都是作为控件给出来的,在RN中也有这么一个控件(Image).根据官网的资料,图片分为本地静态图片,网络图片和混合app资源.一下分类介绍来源官网. ...
- Android加速度传感器
Android加速度传感器 效果图 手机平放桌面的两张截屏,数据一直在刷新 源码 下载地址(Android Studio工程):http://download.csdn.net/detail/q487 ...
- Swift延迟加载的一种用途
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 有以下一种情况: 我们试图用Cocoa的语音合成类NSSpee ...
- 如何找到java对应的c/c++源码
很多时候java经常被c鄙视,因为c说我是你的基础,但java竟然有如此强的生命力就必然有其存在的价值.本文不探讨各种开发语言的优劣,仅仅介绍如何找到java对应c/c++实现的源码.当我们追究一个j ...
- 极光推送---安卓Demo
对于一个一直干.net的程序媛来说,冷不丁的让小编干安卓,那种感觉就好似小狗狗咬小刺猬一样,不知道从哪儿开始下手,对于小编来说,既是挑战更是机遇,因为知识都是相通的,再者来说,在小编的程序人生中,留下 ...
- MyBatis主键生成器KeyGenerator(一)
Mybatis提供了主键生成器接口KeyGenerator,insert语句默认是不返回记录的主键值,而是返回插入的记录条数:如果业务层需要得到记录的主键时,可以通过配置的方式来完成这个功能 . 由于 ...
- iOS中 图文混排/自定义图文混排 作者:韩俊强
指示根视图:(准备几张图片,把label加载在window上) CustomLable *label = [[CustomLable alloc]initWithFrame:CGRectMake(0, ...
- UNIX环境高级编程——epoll函数使用详解
epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是 ...
- JAVA之旅(十九)——ListIterator列表迭代器,List的三个子类对象,Vector的枚举,LinkedList,ArrayList和LinkedList的小练习
JAVA之旅(十九)--ListIterator列表迭代器,List的三个子类对象,Vector的枚举,LinkedList,ArrayList和LinkedList的小练习 关于数据结构,所讲的知识 ...