802.11退避算法与NS2中mac-802_11协议实现的比较分析
我们将利用一段图和一段代码来阐述802.11关于退避(Backoff)算法的协议以及在NS2中的具体实现。
下图出自802.11-2007的文档,简单明了的解释了各个node在竞争信道时发生的故事。
为了便于理解,这里的阐述将直接和NS2中mac-802.11.cc的send()函数相关联。具体见以下此段代码。
1 if(mhBackoff_.busy() == 0) {
2 if(is_idle()) {
3 if (mhDefer_.busy() == 0) {
4 /*
5 * If we are already deferring, there is no
6 * need to reset the Defer timer.
7 */
8 if (bugFix_timer_) {
9 mhBackoff_.start(cw_, is_idle(),
10 phymib_.getDIFS());
11 }
12 else {
13 rTime = (Random::random() % cw_)
14 * (phymib_.getSlotTime());
15 mhDefer_.start(phymib_.getDIFS() +
16 rTime);
17 }
18 }
19 } else {
20 /*
21 * If the medium is NOT IDLE, then we start
22 * the backoff timer.
23 */
24 mhBackoff_.start(cw_, is_idle());
25 }
26 }
当一个node准备send之前,首先要检查自己的backoff timer是否已经开始倒计时,如果已经开始倒计时,那么只要return等待timer的结束即可。因为任何情况下只要mhBackoff timer结束,此node就可以立即占用信道。这对应于node第一行中的判断。
接下来,我们就将对上面的图和代码进行综合解释。
首先,对于node B 来说,他首先check自己的mhBackoff timer,发现not busy;接着检查信道是否空闲(is_idle()),此时因为上图中信道被A占据,所以B被迫进入defer阶段,等待信道的再次空闲。此时,对应的代码段因为is_idle()判断为否,直接进入line 19的else 语句中,设定自己的Backoff timer。
许多人到这里就已经开始不理解,明明这段是defer的时间,怎么会在这里设定backoff timer呢?实际上这里设定的backoff timer是对应于node B紧接下来的那一段backoff timer. 见下图。 具体原因就在于backoff timer class本身的实现方法了。
解释:1.在backoff timer被设置为cw_的瞬间,由于第二个参数is_idle()为0,start()函数会立即将paused_设为1,即立即暂停backoff timer,所以此时实际上并不会开始倒计时,而是要等到resume()函数被唤醒之后才会继续。
2.start(cw_, is_idle())这句有两个疑点。第一,backoff的时间应该是random%cw_*slottime而不是固定的cw_; 第二,在被唤醒之后,除了等待random的backoff 时间,802.11中规定了首先要冻结DIFS之后才可以倒计时backoff timer(参见figure 9-6). 事实上,这两点分别在mac-timers.cc中的BackoffTimer::start(int cw, int idle, double difs) 和BackoffTimer::resume(double difs)中给出了解释。即backoff的时间是在start()中设置的,而resume之后的DIFS是在resume()中加入的。
到此,我们解释完了当信道为忙时,node B采取的backoff措施。 在NS2实现过程中,还会牵扯到NAV timer的设定,不属本文范畴,暂且不提。
对于node C和D也是采取同样的措施。
那么,当信道为空闲时,B、C、D的backoff timer同时被唤醒,执行resume()函数,首先等待DIFS,接着开始timer的countdown,由于C比较幸运,backoff timer率先结束,于是直接占用信道。此时,信道状态变为忙,于是B和D的backoff timer也被迫暂停(图中backoff的灰色部分表示剩余的冻结时间),直到C的数据传输完毕。重复以上过程,接下来D、E和B分别占用信道,整个过程结束。
对于NS2 send()函数中剩余部分的解释如下:
拿下图来说,我们假定此时node B还没有backoff,即忽略下图中Frame前面的那一小段backoff;并且假定node B将要开始传送数据时信道为空闲状态,如下图所示。
那么,对应上述代码,首先check mhBackoff为闲,接着check is_idle()为真,进入mhDefer的判断。如果defer timer已经被前面的将要send出去的数据包设置过了,那么就无需再设,只需要等待他的time out即可。否则的话,开始设置占用信道的backoff timer.这时候引入了一个bugFix_timer_的标志,具体参见http://www.cnblogs.com/flasheryu/articles/2220167.html 此文。
在NS2的default.tcl的设置中,bugFix_timer_是设为真的,所以这时候我们就直接进入mhBackoff_.start(),开始倒计时。但是注意,这时候start()函数里加了一个phymib_.getDIFS(),即加入了一个difs,这与上文所述并不矛盾。因为,上文中所述,信道正处于忙碌状态,backoff 会立即暂停并在resume后由resume()函数加入difs;但此时由于信道为空闲,backoff timer会立即启动,那么自然要手动加入这个difs了。
此处也在上述链接中有所解释。
那么关于代码的主要部分就讲解完毕了。
但是此处遗留了比较严重的疑问,笔者暂时没弄明白。
即在由bugFix_timer_更正了原先使用mhDefer的bug之后,关于判断mhDefer_.busy()==0的问题。接下来就是要搞清楚mhDefer的设置问题了。