在查看NAPI机制的时候发现一篇介绍NAPI引入初衷的文章写的很好,通俗易懂,就想要分享下,重要的是博主还作了能够在他基础上任意修改,而并不用注明出处的声明,着实令我敬佩,不过仍是附上原文连接!linux
http://blog.csdn.net/dog250/article/details/5302853算法
处理外部事件是cpu必需要作的事,由于cpu和外设的不平等性致使外设的事件被cpu 看成是外部事件,其实它们是平等的,只不过冯氏机器不这么认为罢了,既然要处理外部事件,那么就须要必定的方法,方法不止一种,大体有中断和轮询以及一种 混杂又复杂的方式,也就是DMA方式。中断是cpu被动处理的一种方式,也就是说cpu不知道什么时候中断,只要有了中断就会通知cpu,而cpu此时必须停 下一切来处理,而轮询是cpu主动查询并处理的过程,cpu隔一会查询一下外设看有没有事情可作。
咱们看一下这两种方式,中断看似很高效,可是却会遗漏一些数据,避免遗漏的机制要么由硬件实现要么由上层的软件实现,而轮询就没有中断高效了,它会作不少 徒劳的操做,并且必须引入暂存机制,就是说因为cpu不可能在每次查询硬件的时候正好有事情可作,为了避免使请求遗漏,随时到来的请求必须暂存在一个私有的 区域内,只要这些都作好了,轮询是不会形成请求遗漏的,中断在不少中断高频触发的时候会形成大量遗漏和竞争,毕竟只有一个cpu,同一个时间点只能有一个 请求被处理,而轮询因为是cpu分批打包处理请求的,所以不会遗漏。
以上的论述有点像我讨论过的inotify和rsync实现的文件同步,inotify的实现就是中断,很显然有遗漏,而rsync实现的就是轮询,显然 没有遗漏,cpu主动作的事情它本身最明白了,可是它要是被动应对就不会这么明白了,它只是按照规则应对罢了,丝绝不会存在任何策略。若是中断过于频繁也 是很差的,由于cpu必须处理中断,这会致使cpu没有时间作正经事,此时最好用轮询,可是外设活动很缓和的时候,用轮询就不合适了,由于询也是白询,此 时比较适合用中断,但是系统怎么知道什么时候外设活跃什么时候外设缓和呢?啊哈,能够用智能预测算法嘛,以历史值为依据!不,不能那样的,由于这是在内核,内核不 是秀算法的地方,我另外的文章强调过这一点。那么怎么办?好办,仍是约定,就是将中断和轮询相结合,这就是linux网卡驱动中的NAPI的方式,它的设 计十分巧妙,就是在第一个包到来的时候中断,而后关闭中断开始轮询,等某一次轮询完毕后发现没有数据了,那么内核默认这次数据已经传输完毕,短期内不会 再有数据了,那么中止轮询,从新开启中断,这样会减小不少次的中断,虽然某次轮询完毕发现没有数据并不能表明1ms之后不会再有数据,可是刚才说了,要想 使算法简单,必须作一个合理的约定,人性化的约定,若是说加上断定什么状况下百分之九十五的可能不须要轮询了并非不可能,只是维护那个算法的开销太大, 它直接抵消了算法带来的优点。用人的思想考虑,若是一个饭店的服务员不停的从厨房接菜而后送到餐桌,注意是不停的,10秒一趟,可是忽然隔了半分钟没有厨 房的人吆喝接菜,若是你是服务员,难道你还会去窗口等菜吗?反正我不会,我会蹲下来稍微休息一下,即便刚蹲下来就会有新活我也愿意赌一把,虽然输得可能性 很大很大。
如此一来,咱们看一下NAPI解决了什么问题,第一,它限制了中断的数量,一旦有中断过来就停掉中断改成轮询,这样就不会形成cpu被频繁中断,第 二,cpu不会作无用功,就是所谓的无用的轮询,由于只有在中断来了才改成轮询,中断来了说明有事可作,看看NAPI将中断和轮询结合的是多么巧妙啊。以 往的实现中,在硬件网卡中断中将skb排入队,而后在软中断中出队并交由上层处理,一切配合的看起来那么好,但是在遇到突发快速小包传输的时候就会致使频 繁中断,由于是突发的包,所以不能用轮询,由于是快速小包,所以不适合用中断,最终两者巧妙结合,各取优点,优点互补,绝了!这个框架适合一切的网卡模 式,所以就将传统的网卡收发机制也归入到了NAPI框架,很简单,就是用原来的逻辑实现dev的poll回调函数便可,至于传统的非NAPI方案,彻底可 以用一个桩子代替。
cpu利用率和频繁的中断问题经过NAPI机制解决了,可是这又引入了一个新的问题,就是这可能形成cpu利用率的失衡,这个怎么理解呢?NAPI启动之 后,网卡的中断就会变得不多,要知道中断balance的目前实现是基于中断数量的均衡,它根本无论中断数量均衡以后引发的softirq致使的cpu使 用率是否均衡,softirq的均衡也是同样,好比一个是磁盘softirq,一个是网卡的NAPI的softirq,前者瞬间就能够完成可是来得频繁, 然后者要轮询网卡而且处理协议栈很耗时可是来得不频繁,但是balancer无论这么多,它只是以为磁盘的softirq太多了而为了数量均衡会将更多的 softirq发布到softiqr少的cpu上,它根本不在意这些更多的softirq是否会带来更高的cpu负载。NAPI削弱了中断/软中断均衡的 做用,毕竟它的主导在轮询,轮询会占用不少的处理器资源,而中断和软中断数量不多。中断或者软中断特别是软中断数量在cpu间的均衡可能形成各个cpu负 载的严重不均衡,由于各个硬中断几乎都是瞬间完成的,硬中断不能耽搁过久的,可是各个不一样软中断的任务量缺是千差万别的,所以绝对不能按照数量来均衡软中 断,然而通常都是硬中断触发软中断,它们都在同一个cpu上,所以若是想简单的实现NAPI在多cpu上的cpu使用率均衡,那么必须从新实现硬件的负载 均衡机制,这样能够吗?不!所以这样会使得两个部分耦合太重,所以必须让硬中断的均衡和cpu的均衡解耦合,其实如今的内核就是这么作的,因此才会形成 cpu不均衡,硬件中断的均衡和cpu均衡的解耦合带来的好处就是咱们能够对软中断均衡作文章,而硬中断的负载均衡仍是用数量均衡实现,软中断完全从硬件 中断中解放出来,再也不是在处理硬中断的cpu上触发软中断,而是能够在任何cpu上触发软中断,因为不一样软中断的任务量千差万别,所以咱们定义一个软中断 的“权值”,而后按照不一样软中断这个权值和数量的积的和来均衡软中断,这样的话,我想各个cpu的负载就均衡了,如今问题是,各个不一样的软中断的“权值” 的计算问题,呵呵。累了,有时间再说。一个论坛上一哥们儿写了一个patch,颇有创意,比我这里的软中断均衡的粒度要小得多,这个补丁不是均衡软中断, 而是将软中断进一步也分红了上下两部分,和cpu相关的上半部必须加急处理,这样不会对cpu形成太大负载,仍然用硬件中断均衡,由于硬件中断的cpu触 发软件中断,这部分不变,可是软中断的下半部就须要均衡了,该补丁为每个cpu创立了一个工做队列,而后将ip_rcv 这种操做的cpu相关的工做放到软中断的上半部,其实就是从一个cpu的skb队列中抽取一个skb,而后将这个skb随机放到这些工做队列中进行处理, 和整个软中断均衡有何不一样吗?大大不一样。软中断均衡针对的是一个poll_list里面的全部的skb,而这哥们儿的补丁针对的是一个skb,粒度十分 小,可是没有测试,是否是过小了呢?这其实也是一个模式方法,逐步的将粒度精细化,相似将中断分红上半部和下半部的作法是放之四海而皆准的,这是一种哲 学,也是一种风格。
若是你说你没有见过linux的方式,那么只要你上过枯燥的计算机课或者读过枯燥的教科书或者你是天才你就知道一个叫作生产者/消费者的模型,它其实和 linux的中断的上半部和下半部很相似,上半部是生产者,只管将环境搭建好,数据准备好,而后触发下半部,其实就是唤醒消费者,这个思想在多线程并发中 很著名,而并发就是为了提升系统吞吐量,在SMP环境中也是为了并发,所以咱们未尝不用用生产者/消费者模型呢?它也是一种低耦合的各司其职的模型。若是 你想不到NAPI的中断+轮询的方式,那么你据说过linux下怎样作文件同步的吗?rsync+inotify的方式据说过吗?若是没有就赶快 google一下吧。rsync+inotify其实就是中断+轮询,rsync是轮询,而inotify是中断,这个同步方案十分高效,保证只有在 inotify监控到文件变化的时候才开始轮询,平时就睡觉,inotify再也不须要监控到具体的文件,由于它只负责告知事件,具体工做由rsync完 成,inotify只须要告诉一端文件变化了便可,那岂不是要所有同步了即便你只改了一个字符,别忘了rsync的算法,这就是另外一篇文章了。因此不要再 以为linux内核深不可测了,它的特色只有一个就是简单,比起用户应用那些复杂的算法,内核的算法一贯简单易懂,其实内核的每个机制,均可以在用户空 间找到原型的。
但是cpu对NAPI处理的均衡真的有意义吗?用户难道就不能忍受一个cpu占用100%而另外一个0%吗?表面上看是那样的,可是若是考虑cache或者 切换代价的话就不同了,性能因素不只仅是cpu使用率还有别的,若是一件事的开销过大,即便cpu使用率再均衡也是划不来的。这就好像初学者用free 命令看内存时老是吓一大跳。特别是NAPI的网络数据包操做,好比TCP的IP包的分段重组问题,一旦乱序就要重传,这种状况下,一个linux主机若是只是做为一台路由器的话,那 么进入系统的一个TCP包的不一样分段若是被不一样的cpu处理并向一个网卡转发了,那么同步问题会很麻烦的,若是你不作同步处理,那么极可能后面的段被一个 cpu先发出去了,那么在真正的接收方接收到乱序的包后就会请求重发,这是不但愿的,所以仍是一个cpu串行处理好,这也许是TCP/IP协议栈的缺陷, 可是没有办法,协议是那样的,只能那样去应对。在大流量下,因为napi的缘故,网卡的中断被关闭了,此时那个第一次被中断的cpu正在poll这个网卡,所以全部的流量都会汇集到这个cpu上,这多是一个设计的缺陷吧。api