概述

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设备结构

  1. /* Structure for NAPI scheduling similar to tasklet but with weighting */
  2. struct napi_struct {
  3. /* The poll_list must only be managed by the entity which changes the
  4. * state of the NAPI_STATE_SCHED bit. This means whoever atomically
  5. * sets that bit can add this napi_struct to the per-cpu poll_list, and
  6. * whoever clears that bit can remove from the list right before clearing the bit.
  7. */
  8. struct list_head poll_list; /* 用于加入处于轮询状态的设备队列 */
  9. unsigned long state; /* 设备的状态 */
  10. int weight; /* 每次处理的最大数量,非NAPI默认为64 */
  11. int (*poll) (struct napi_struct *, int); /* 此设备的轮询方法,非NAPI为process_backlog() */
  12. #ifdef CONFIG_NETPOLL
  13. ...
  14. #endif
  15. unsigned int gro_count;
  16. struct net_device *dev;
  17. struct list_head dev_list;
  18. struct sk_buff *gro_list;
  19. struct sk_buff *skb;
  20. };

(2) 初始化

初始napi_struct实例。

  1. void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
  2. int (*poll) (struct napi_struct *, int), int weight)
  3. {
  4. INIT_LIST_HEAD(&napi->poll_list);
  5. napi->gro_count = 0;
  6. napi->gro_list = NULL;
  7. napi->skb = NULL;
  8. napi->poll = poll; /* 设备的poll函数 */
  9. napi->weight = weight; /* 设备每次poll能处理的数据包个数上限 */
  10. list_add(&napi->dev_list, &dev->napi_list); /* 加入设备的napi_list */
  11. napi->dev = dev; /* 所属设备 */
  12. #ifdef CONFIG_NETPOLL
  13. spin_lock_init(&napi->poll_lock);
  14. napi->poll_owner = -1;
  15. #endif
  16. set_bit(NAPI_STATE_SCHED, &napi->state); /* 设置NAPI标志位 */
  17. }

(3) 调度

在网卡驱动的中断处理函数中调用napi_schedule()来使用NAPI。

  1. /**
  2. * napi_schedule - schedule NAPI poll
  3. * @n: napi context
  4. * Schedule NAPI poll routine to be called if it is not already running.
  5. */
  6. static inline void napi_schedule(struct napi_struct *n)
  7. {
  8. /* 判断是否可以调度NAPI */
  9. if (napi_schedule_prep(n))
  10. __napi_schedule(n);
  11. }

判断NAPI是否可以调度。如果NAPI没有被禁止,且不存在已被调度的NAPI,

则允许调度NAPI,因为同一时刻只允许有一个NAPI poll instance。

  1. /**
  2. * napi_schedule_prep - check if napi can be scheduled
  3. * @n: napi context
  4. * Test if NAPI routine is already running, and if not mark it as running.
  5. * This is used as a condition variable insure only one NAPI poll instance runs.
  6. * We also make sure there is no pending NAPI disable.
  7. */
  8. static inline int napi_schedule_prep(struct napi_struct *n)
  9. {
  10. return !napi_disable_pending(n) && !test_and_set_bit(NAPI_STATE_SCHED, &n->state);
  11. }
  12. static inline int napi_disable_pending(struct napi_struct *n)
  13. {
  14. return test_bit(NAPI_STATE_DISABLE, &n->state);
  15. }
  16. enum {
  17. NAPI_STATE_SCHED, /* Poll is scheduled */
  18. NAPI_STATE_DISABLE, /* Disable pending */
  19. NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
  20. };

NAPI的调度函数。把设备的napi_struct实例添加到当前CPU的softnet_data的poll_list中,

以便于接下来进行轮询。然后设置NET_RX_SOFTIRQ标志位来触发软中断。

  1. void __napi_schedule(struct napi_struct *n)
  2. {
  3. unsigned long flags;
  4. local_irq_save(flags);
  5. ____napi_schedule(&__get_cpu_var(softnet_data), n);
  6. local_irq_restore(flags);
  7. }
  8. static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi)
  9. {
  10. /* 把napi_struct添加到softnet_data的poll_list中 */
  11. list_add_tail(&napi->poll_list, &sd->poll_list);
  12. __raise_softirq_irqoff(NET_RX_SOFTIRQ); /* 设置软中断标志位 */
  13. }

(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并提交给上层。

  1. /**
  2. * netif_receive_skb - process receive buffer from network
  3. * @skb: buffer to process
  4. * netif_receive_skb() is the main receive data processing function.
  5. * It always succeeds. The buffer may be dropped during processing
  6. * for congestion control or by the protocol layers.
  7. * This function may only be called from softirq context and interrupts
  8. * should be enabled.
  9. * Return values (usually ignored):
  10. * NET_RX_SUCCESS: no congestion
  11. * NET_RX_DROP: packet was dropped
  12. */
  13. int netif_receive_skb(struct sk_buff *skb)
  14. {
  15. /* 记录接收时间到skb->tstamp */
  16. if (netdev_tstamp_prequeue)
  17. net_timestamp_check(skb);
  18. if (skb_defer_rx_timestamp(skb))
  19. return NET_RX_SUCCESS;
  20. #ifdef CONFIG_RPS
  21. ...
  22. #else
  23. return __netif_receive_skb(skb);
  24. #endif
  25. }

__netif_receive_skb()在上篇blog中已分析过了,接下来就是网络层来处理接收到的数据包了。

NAPI之(一)——原理和实现相关推荐

  1. linux支持pci-e硬盘吗,Linux下基于PCI-E接口的固态硬盘驱动设计与实现

    摘要: 随着信息技术的不断发展和创新,特别是自互联网出现以来,人类社会的信息总量每日剧增,呈现爆炸式的增长.人们对信息存储的效率也有了更高的要求,不再满足于单纯的容量提升.固态硬盘作为一个在存储领域刚 ...

  2. NAPI技术--原理和实现(一)

    概述 NAPI是linux新的网卡数据处理API,据说是由于找不到更好的名字,所以就叫NAPI(New API),在2.5之后引入. 简单来说,NAPI是综合中断方式与轮询方式的技术. 中断的好处是响 ...

  3. Linux内核NAPI机制分析

    转自:http://blog.chinaunix.net/uid-17150-id-2824051.html 简介: NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采 ...

  4. Linux Kernel TCP/IP Stack — L1 Layer — NIC Controller — NAPI

    目录 文章目录 目录 NAPI 中断方式与轮询方式 NAPI 值得注意的是,传统收包方式是每个报文都会触发中断,如果中断太频繁,CPU 就总是处理中断,其他任务无法得到调度,于是 NAPI(New A ...

  5. Linux 内核网络协议栈运行原理

    封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络.对于每一层而言,对收到的数据都会封装相应的协议首部信息( ...

  6. centos 7 局域网丢包排查_ethtool原理介绍和解决网卡丢包排查思路

    前言 之前记录过处理因为LVS网卡流量负载过高导致软中断发生丢包的问题,RPS和RFS网卡多队列性能调优实践,对一般人来说压力不大的情况下其实碰见的概率并不高.这次想分享的话题是比较常见服务器网卡丢包 ...

  7. epoll背后的原理

    1 简介 Epoll 是个很老的知识点,是后端工程师的经典必修课.这种知识具备的特点就是研究的人多,所以研究的趋势就会越来越深.当然分享的人也多,由于分享者水平参差不齐,也产生的大量错误理解. 今天我 ...

  8. Nginx工作原理及相关介绍

    Nginx工作原理及相关介绍 一.Nginx工作原理与模块介绍 1.Nginx基本工作原理 NGINX以高性能的负载均衡器,缓存,和web服务器闻名.Nginx由内核和模块组成,其中,内核的设计非常微 ...

  9. linux报文高速捕获技术对比--napi/libpcap/afpacket/pfring/dpdk/xdp

    1. 传统linux网络协议栈流程和性能分析 Linux网络协议栈是处理网络数据包的典型系统,它包含了从物理层直到应用层的全过程. 数据包到达网卡设备. 网卡设备依据配置进行DMA操作.(第1次拷贝: ...

最新文章

  1. 计算机加入到域的注意事项
  2. android 应用启动不了,不能断点
  3. 围观窗体与组件01 - 零基础入门学习Delphi23
  4. boost::geometry::intersects用法的测试程序
  5. ITK:图像重新取样
  6. 快速git本地项目到github的关键命令及执行步骤(附上idea到git的步骤)
  7. JavaScript 更新对象属性
  8. 河南理工大学c语言报告封面,河南理工大学图书信息管理系统设计_纯c语言课程设计.doc...
  9. Python数值类型
  10. Django框架基础之session
  11. 映射文件实现进程通信
  12. 安卓双进程保活的代码
  13. 下载网页中的html5视频之手动方法
  14. Java完全自学手册,一册在手,offer我有(附程序员书单)
  15. 插头dp ——从入门到跳楼
  16. lidar_camera_calib操作流程记录
  17. 如何用 XMind 做商业计划书?
  18. 旋转编码器旋钮程序_让我们使用SwiftUI构建具有旋转手势的复古音频旋钮
  19. Ubuntu下利用docker安装微信
  20. java对象转换String类型的三种方法

热门文章

  1. 计算机学院元旦晚会对联,学校元旦对联加横批
  2. 如何批量输出条形码图片
  3. python函数map和split函数
  4. 远程桌面连接是什么?远程桌面连接使用教程
  5. 9104年了,你还中毒?—— Synaptics.exe 中毒小记
  6. 出现了,PPT 制作新方式
  7. 无线鼠标迟钝但并不是电量问题
  8. Istio,下一个Kubernetes?
  9. pytorch加载自己的数据集,数据集载入-视频合集
  10. PCA9685--16路 PWM模块舵机驱动板--STM32 IIC接口模块