处理外部事件是cpu必须要做的事,因为cpu和外设的不平等性导致外设的事件被cpu 当作是外部事件,其实它们是平等的,只不过冯氏机器不这么认为罢了,既然要处理外部事件,那么就需要一定的方法,方法不止一种,大致有中断和轮询以及一种 混杂又复杂的方式,也就是DMA方式。中断是cpu被动处理的一种方式,也就是说cpu不知道何时中断,只要有了中断就会通知cpu,而cpu此时必须停 下一切来处理,而轮询是cpu主动查询并处理的过程,cpu隔一会查询一下外设看有没有事情可做。
我们看一下这两种方式,中断看似很高效,但是却会遗漏一些数据,避免遗漏的机制要么由硬件实现要么由上层的软件实现,而轮询就没有中断高效了,它会做很多 徒劳的操作,而且必须引入暂存机制,就是说由于cpu不可能在每次查询硬件的时候正好有事情可做,为了不使请求遗漏,随时到来的请求必须暂存在一个私有的 区域内,只要这些都做好了,轮询是不会造成请求遗漏的,中断在很多中断高频触发的时候会造成大量遗漏和竞争,毕竟只有一个cpu,同一个时间点只能有一个 请求被处理,而轮询由于是cpu分批打包处理请求的,因此不会遗漏。
以上的论述有点像我讨论过的inotify和rsync实现的文件同步,inotify的实现就是中断,很显然有遗漏,而rsync实现的就是轮询,显然 没有遗漏,cpu主动做的事情它自己最明白了,但是它要是被动应对就不会这么明白了,它只是按照规则应对罢了,丝毫不会存在任何策略。如果中断过于频繁也 是不好的,因为cpu必须处理中断,这会导致cpu没有时间做正经事,此时最好用轮询,但是外设活动很缓和的时候,用轮询就不合适了,因为询也是白询,此 时比较适合用中断,可是系统怎么知道何时外设活跃何时外设缓和呢?啊哈,可以用智能预测算法嘛,以历史值为依据!不,不能那样的,因为这是在内核,内核不 是秀算法的地方,我另外的文章强调过这一点。那么怎么办?好办,还是约定,就是将中断和轮询相结合,这就是linux网卡驱动中的NAPI的方式,它的设 计十分巧妙,就是在第一个包到来的时候中断,然后关闭中断开始轮询,等某一次轮询完毕后发现没有数据了,那么内核默认此次数据已经传输完毕,短时间内不会 再有数据了,那么停止轮询,重新开启中断,这样会减少很多次的中断,虽然某次轮询完毕发现没有数据并不能代表1ms以后不会再有数据,但是刚才说了,要想 使算法简单,必须做一个合理的约定,人性化的约定,如果说加上判定什么情况下百分之九十五的可能不需要轮询了并不是不可能,只是维护那个算法的开销太大, 它直接抵消了算法带来的优势。用人的思想考虑,如果一个饭店的服务员不停的从厨房接菜然后送到餐桌,注意是不停的,10秒一趟,但是突然隔了半分钟没有厨 房的人吆喝接菜,如果你是服务员,难道你还会去窗口等菜吗?反正我不会,我会蹲下来稍微休息一下,即使刚蹲下来就会有新活我也愿意赌一把,虽然输得可能性 很大很大。
我们看一下这两种方式,中断看似很高效,但是却会遗漏一些数据,避免遗漏的机制要么由硬件实现要么由上层的软件实现,而轮询就没有中断高效了,它会做很多 徒劳的操作,而且必须引入暂存机制,就是说由于cpu不可能在每次查询硬件的时候正好有事情可做,为了不使请求遗漏,随时到来的请求必须暂存在一个私有的 区域内,只要这些都做好了,轮询是不会造成请求遗漏的,中断在很多中断高频触发的时候会造成大量遗漏和竞争,毕竟只有一个cpu,同一个时间点只能有一个 请求被处理,而轮询由于是cpu分批打包处理请求的,因此不会遗漏。
以上的论述有点像我讨论过的inotify和rsync实现的文件同步,inotify的实现就是中断,很显然有遗漏,而rsync实现的就是轮询,显然 没有遗漏,cpu主动做的事情它自己最明白了,但是它要是被动应对就不会这么明白了,它只是按照规则应对罢了,丝毫不会存在任何策略。如果中断过于频繁也 是不好的,因为cpu必须处理中断,这会导致cpu没有时间做正经事,此时最好用轮询,但是外设活动很缓和的时候,用轮询就不合适了,因为询也是白询,此 时比较适合用中断,可是系统怎么知道何时外设活跃何时外设缓和呢?啊哈,可以用智能预测算法嘛,以历史值为依据!不,不能那样的,因为这是在内核,内核不 是秀算法的地方,我另外的文章强调过这一点。那么怎么办?好办,还是约定,就是将中断和轮询相结合,这就是linux网卡驱动中的NAPI的方式,它的设 计十分巧妙,就是在第一个包到来的时候中断,然后关闭中断开始轮询,等某一次轮询完毕后发现没有数据了,那么内核默认此次数据已经传输完毕,短时间内不会 再有数据了,那么停止轮询,重新开启中断,这样会减少很多次的中断,虽然某次轮询完毕发现没有数据并不能代表1ms以后不会再有数据,但是刚才说了,要想 使算法简单,必须做一个合理的约定,人性化的约定,如果说加上判定什么情况下百分之九十五的可能不需要轮询了并不是不可能,只是维护那个算法的开销太大, 它直接抵消了算法带来的优势。用人的思想考虑,如果一个饭店的服务员不停的从厨房接菜然后送到餐桌,注意是不停的,10秒一趟,但是突然隔了半分钟没有厨 房的人吆喝接菜,如果你是服务员,难道你还会去窗口等菜吗?反正我不会,我会蹲下来稍微休息一下,即使刚蹲下来就会有新活我也愿意赌一把,虽然输得可能性 很大很大。
如此一来,我们看一下NAPI解决了什么问题,第一,它限制了中断的数量,一旦有中断过来就停掉中断改为轮询,这样就不会造成cpu被频繁中断,第 二,cpu不会做无用功,就是所谓的无用的轮询,因为只有在中断来了才改为轮询,中断来了说明有事可做,看看NAPI将中断和轮询结合的是多么巧妙啊。以 往的实现中,在硬件网卡中断中将skb排入队,然后在软中断中出队并交由上层处理,一切配合的看起来那么好,可是在遇到突发快速小包传输的时候就会导致频 繁中断,因为是突发的包,因此不能用轮询,因为是快速小包,因此不适合用中断,最终二者巧妙结合,各取优势,优势互补,绝了!这个框架适合一切的网卡模 式,因此就将传统的网卡收发机制也纳入到了NAPI框架,很简单,就是用原来的逻辑实现dev的poll回调函数即可,至于传统的非NAPI方案,完全可 以用一个桩子代替。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1274140