《网络设备轮询设计与实现》由会员分享,可在线阅读,更多相关《网络设备轮询设计与实现(17页珍藏版)》请在金锄头文库上搜索。
1、网络设备轮询设计与实现-BSD 操作系统核心新技术作者:xie_minix(谢小荣)1.1 引言polling(轮询) 技术是一种在一定时间内不断的探测目标,以获取目标在满足条件后进行相应处理的技术。从大多数情况来讲,这种技术并没有以中断为代表的技术先进。但是在一些特定场合,如主机作为一个大型的网关,并且流量巨大,选用轮询技术还是比较合适的。本文是针对 FreeBSD 操作系统及其代码来进行分析其设计思路和实现过程。目前 BSD 类操作系统中只有 FreeBSD4.10和 FreeBSD5.3 以上版本才加入了对轮询的支持,NetBSD 和 OpenBSD 还未实现。作为大多数防火墙首选的操作
2、系统 OpenBSD 到是应该实现轮询技术。在TCP/IP 详解第二卷中,书中作者曾经提到过该技术,而且好象冯.杰克布森已经在一些实验中实现过。直到不久前我们才看到这些代码在FreeBSD 中实现,要知道在核心中加入一项新的技术工作量是非常之大,轮询代码不但是要在核心中增加一些代码文件,而且把所有网卡驱动程序都要进行大量相应的改动。我们以 VIA Rhine(威盛的莱因型芯片,通常的 DLINK 530TX 网卡使用的芯片)的驱动程序 if_vr.c 和他的头文件 if_vrreg.h(后面具体涉及到硬件驱动程序的代码都以 vr 代码做实例分析) 来简单讲述代码的一些改动。A XX_softc
3、 结构中的改动:XX 是代表各芯片的简称,本文使用的是 vr 芯片代码,所以是vr_softc 结构。在结构的最后部分,增加了以下代码:#ifdef DEVICE_POLLINGint rxcycles;#endif以上代码出现在源代码 if_vrreg.h 中的第 472 行中。DEVICE_POLLING是由核心配置决定。如果核心配置文件中加入了该定义,则在#ifdef 和#endif 行之间的代码将编译进核心。也就是说,所有的支持 POLLING 的代码都应该在#ifdef DEVICE_POLLING 定义之下和#endif 之上。图中的 rxcycles 变量是用来存放在核心进入 v
4、r 驱动程序时,POLLING 要求驱动程序的底半部分(即读出和写入)进行多少次的循环。B 在连接该设备驱动程序到设备程序链表时的一些改动:任何以太网络设备在探测到该设备存在后(xx_probe 函数,xx 在这是代表芯片在系统中的简称),紧接着执行设备链入函数(xx_attach),他的工作主要是进行硬件的一些初始化(中断的分配、网卡缓存的内存映射等等)和 ifnet 结构的初始化。#ifdef DEVICE_POLLINGifp-if_capabilities |= IFCAP_POLLING;#endif上图是 if_vr.c 中的第 742 行中关于对 POLLING 支持时候的一段代
5、码。if_capabilities 成员是用来判断网卡支持性能的成员。比如对 VLAN 技术的支持,POLLING 技术的支持等标志都是放在此成员中。1.2 代码介绍所有的轮询代码主体在 1 个 C 文件和各支持轮询的网络设备驱动程序中,以及一些相关的支撑文件。图 1-1 中没有列出所有的网络设备驱动程序。文件 说明net/if_var.hpci/if_vrreg.h申明一些函数修改 ifnet 结构以支持轮询kern/kern_poll.ckern/kern_et/netisr.cpci/if_vr.c轮询的核心部分代码一段定时调用 kern_poll.c 中轮询代码网络软中断代码网络设备驱
6、动程序(基于 vr 芯片的卡)其他的网络设备驱动程序图 1-1 本文包含的文件1.2.1 全局变量从第 106 行开始,都是 POLLING 相关的全局变量.首先是建立一 SYSCTL树的节点.下图中的 SYSCTL_NODE 宏代表在父节点 _kern 下建立一个_kern_polling 节点.SYSCTL_NODE(_kern, OID_AUTO, polling, CTLFLAG_RW, 0,Device polling parameters);下图中的变量全部可用 sysctl 来查看,大部分都可以调整设置.这些全局变量都是使用宏 SYSCTL_UINT 把他们加入到 _kern_p
7、olling 节点下,成为该节点的叶子.之所以这样,是因为可以通过用户区来调整这些参数.static u_int32_t poll_burst = 5; - kern_poll.cstatic u_int32_t poll_each_burst = 5;static u_int32_t poll_burst_max = 150;static u_int32_t poll_in_idle_loop=0;u_int32_t poll_in_trap;static u_int32_t user_frac = 50;static u_int32_t reg_frac = 20 ;static u_in
8、t32_t short_ticks;static u_int32_t lost_polls;static u_int32_t pending_polls;static int residual_burst = 0;static u_int32_t poll_handlers;static int polling = 0;static u_int32_t phase;static u_int32_t suspect;static u_int32_t stalled;static u_int32_t idlepoll_sleeping; - kern_poll.c图 1-2-2在图 1-2-2 中
9、.这些变量按功能分可分为 3 大类.A. 开关型:polling: 初始为 0,即默认为不打开轮询功能.poll_in_idle_loop:该参数用于 poll_idle 函数,用来确定是否进入一低优先权的循环轮询中.即 CPU 空闲时是否来执行轮询.poll_in_trap:此参数不但是在陷入时是否执行轮询的开关,而且其值也是在陷入(trap)时执行多少次轮询B. 算法参数poll_each_burst:一个基本的轮询次数 ,很多其他变量都来和他进行比较 (主要是空闲时执行轮询或陷入时执行轮询).该值系统默认是 5,poll_burst:一个动态的轮询次数,主要用于根据核心( 轮询)占用的时
10、间片调整轮询次数,在核心(轮询) 时间片小于预定的时间片时,该值加 1,当核心(轮询)时间片过长,导致丢失一个或更多的时钟嘀嗒时,该值将减去该值的 8 分之 1.这种算法是 FreeBSD中轮询技术的主要算法.当然有一定的局限性.当网络分组快速增加时,此算法只是加1 来增加次数再来调用软件中断,从而形成软中断暴增的做法并不好.而且在占用时间片过长时的减 8 分之 1 的做法也缺少理论依据.(而且感觉根本站不住脚).poll_burst_max:轮训的最大值,该值是用来限制 poll_burst 在累加过程中的最大量.当然该值可以调整.他的调整范围在后面讲的两个宏之内.user_frac:用户区
11、占用的 CPU 时间片的百分比.该值用来确定核心(轮询)所占时间片是否有剩余,如果有的话就调整动态值 poll_burst,使其加 1.residual_burst:在正常的轮询次数中,都是以 poll_each_burst 为标准的,而当动态的poll_burstpoll_each_burst 时候,就会产生剩余没轮询的次数,该次数就是residual_burst,当然其结果就是继续轮询完 residual_burst.idlepoll_sleeping:该值的使用前提是 poll_in_idle_loop 变量开关已经打开,即在 CPU空闲时支持轮询.系统置该值为 0,可以直接进入到 CP
12、U 空闲时的轮询代码中;如果poll_in_idle_loop 变量开关还没开放,系统会给该值置 1,也就是说.该值其实是一个CPU 空闲时是否进入轮询代码的状态,reg_frac:在整个循环代码执行了该值的次数之后,就进行检查网卡的状态寄存器.看看网卡是否有什么问题.pending_polls:在进入轮询前(我们有个时钟嘀嗒钩子函数),此参数加 1,再轮询后会对其减 1,再次进入时钟嘀嗒后半部分会判断是否平衡,如果由于轮询时间过长,此次嘀嗒便会错过.C. 调试与参考short_ticks:每次时钟嘀嗒钩子函数所花的时间如果小于 5000 毫秒.那这种间隔时间太短了.这是以 HZ=100 来计
13、算的.源代码作者认为.在 100M 卡时,HZ 数调整到 1000比较合适,那么我们的时钟嘀嗒钩子函数所花时间在小于 500 毫秒是合适的.lost_polls: 由于 pending_polls 的不平衡.记录一次嘀嗒时间错过,该值会不停的累加,给系统管理员提示可以调整 poll_burst_max 值小一些.或根据情况把 user_frac(用户占用 CPU 时间片的百分比)的值调的更小些.poll_handlers:有多少个网络设备支持并注册了轮询.phase:指示轮询进行到了哪个阶段.轮询代码共分为 6 个阶段.0 阶段代表是初始阶段或上次轮询的结束.1 阶段时钟嘀嗒钩子函数在设置网络
14、软中断(轮询中断)前2 阶段时钟嘀嗒钩子函数在设置网络软中断(轮询中断)后.3 阶段是进入软中断 netisr_poll 前.4 阶段是从软中断 netisr_poll 中出来.5 阶段是进入软中断 netisr_pollmore 前6 阶段是从软中断 netisr_pollmore 中出来.suspect:由于最后的软中断 netisr_pollmore 在处理时会再完成时把阶段标志 phase置为 0,或出现其他未完成情况时进入阶段 5 和 6.那么就是说.我们的时钟嘀嗒钩子在最初时小于 2 阶段的话,一定是在 0 阶段.如果出现 1 或 2 阶段的话就有问题了.此值在出现这种问题时进行记
15、录.stalled:由于 pending_polls 太大( 大于 100),也就是说由于每次轮询时间过长,以至于轮询丢失了太多嘀嗒,当到达 100 次时,该值加 1.(源代码的作者认为不会发生此类事情,除了在网卡激活时)1.2.2 数据结构在轮询核心代码 kern_poll.c 中,数据结构只有 pollrec 一个.该结构只有两个成员.第一个成员 handler 是一指针.指向网络设备驱动程序中的处理轮询的函数,另一个是 ifp,是一个网络设备的 ifnet 结构指针.每一支持轮询的网络设备在轮询注册的过程中来初始化其 pollrec 结构.见图 1-2-2-1struct pollrec - kern_poll.cpoll_handler_t *handler;struct ifnet *ifp;图 1-2-2-1pollrec 结构在系统中实际上有个长 128 个成员的结构数组 .static struct pollrec prPOLL_LIST_LEN; - kern_poll.c每注册一次,数组指针加 1.当前位置记录在 1.2.1