linux0.11源代码电梯算法解析

转载自http://blog.youkuaiyun.com/suppercoder/article/details/19619777

如果涉及版权,请通知我,本人将立即删除,谢谢!

在看linux0.11代码里面的电梯算法的时候,产生了一些疑惑,经过分析解决了,发现网上也有不少讨论,我自己的分析记录下来。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00017 int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)  
  2. 00018 {  
  3. 00019     int left,chars,nr;  
  4. 00020     struct buffer_head * bh;  
  5. 00021   
  6. 00022     if ((left=count)<=0)  
  7. 00023         return 0;  
  8. 00024     while (left) {  
  9. 00025         if ((nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE))) {  
  10. 00026             if (!(bh=bread(inode->i_dev,nr)))  
  11. 00027                 break;  
  12. 00028         } else  
  13. 00029             bh = NULL;  
  14. 00030         nr = filp->f_pos % BLOCK_SIZE;  
  15. 00031         chars = MIN( BLOCK_SIZE-nr , left );  
  16. 00032         filp->f_pos += chars;  
  17. 00033         left -= chars;  
  18. 00034         if (bh) {  
  19. 00035             char * p = nr + bh->b_data;  
  20. 00036             while (chars-->0)  
  21. 00037                 put_fs_byte(*(p++),buf++);  
  22. 00038             brelse(bh);  
  23. 00039         } else {  
  24. 00040             while (chars-->0)  
  25. 00041                 put_fs_byte(0,buf++);  
  26. 00042         }  
  27. 00043     }  
  28. 00044     inode->i_atime = CURRENT_TIME;  
  29. 00045     return (count-left)?(count-left):-ERROR;  
  30. 00046 }  


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00270 struct buffer_head * bread(int dev,int block)  
  2. 00271 {  
  3. 00272     struct buffer_head * bh;  
  4. 00273   
  5. 00274     if (!(bh=getblk(dev,block)))  
  6. 00275         panic("bread: getblk returned NULL\n");  
  7. 00276     if (bh->b_uptodate)  
  8. 00277         return bh;  
  9. 00278     ll_rw_block(READ,bh);  
  10. 00279     wait_on_buffer(bh);  
  11. 00280     if (bh->b_uptodate)  
  12. 00281         return bh;  
  13. 00282     brelse(bh);  
  14. 00283     return NULL;  
  15. 00284 }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00145 void ll_rw_block(int rw, struct buffer_head * bh)  
  2. 00146 {  
  3. 00147     unsigned int major;  
  4. 00148   
  5. 00149     if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||  
  6. 00150     !(blk_dev[major].request_fn)) {  
  7. 00151         printk("Trying to read nonexistent block-device\n\r");  
  8. 00152         return;  
  9. 00153     }  
  10. 00154     make_request(major,rw,bh);  
  11. 00155 }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00088 static void make_request(int major,int rw, struct buffer_head * bh)  
  2. 00089 {  
  3. 00090     struct request * req;  
  4. 00091     int rw_ahead;  
  5. 00092   
  6. 00093 /* WRITEA/READA is special case - it is not really needed, so if the */  
  7. 00094 /* buffer is locked, we just forget about it, else it's a normal read */  
  8. 00095     if ((rw_ahead = (rw == READA || rw == WRITEA))) {  
  9. 00096         if (bh->b_lock)  
  10. 00097             return;  
  11. 00098         if (rw == READA)  
  12. 00099             rw = READ;  
  13. 00100         else  
  14. 00101             rw = WRITE;  
  15. 00102     }  
  16. 00103     if (rw!=READ && rw!=WRITE)  
  17. 00104         panic("Bad block dev command, must be R/W/RA/WA");  
  18. 00105     lock_buffer(bh);  
  19. 00106     if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {  
  20. 00107         unlock_buffer(bh);  
  21. 00108         return;  
  22. 00109     }  
  23. 00110 repeat:  
  24. 00111 /* we don't allow the write-requests to fill up the queue completely: 
  25. 00112  * we want some room for reads: they take precedence. The last third 
  26. 00113  * of the requests are only for reads. 
  27. 00114  */  
  28. 00115     if (rw == READ)  
  29. 00116         req = request+NR_REQUEST;  
  30. 00117     else  
  31. 00118         req = request+((NR_REQUEST*2)/3);  
  32. 00119 /* find an empty request */  
  33. 00120     while (--req >= request)  
  34. 00121         if (req->dev<0)  
  35. 00122             break;  
  36. 00123 /* if none found, sleep on new requests: check for rw_ahead */  
  37. 00124     if (req < request) {  
  38. 00125         if (rw_ahead) {  
  39. 00126             unlock_buffer(bh);  
  40. 00127             return;  
  41. 00128         }  
  42. 00129         sleep_on(&wait_for_request);  
  43. 00130         goto repeat;  
  44. 00131     }  
  45. 00132 /* fill up the request-info, and add it to the queue */  
  46. 00133     req->dev = bh->b_dev;  
  47. 00134     req->cmd = rw;  
  48. 00135     req->errors=0;  
  49. 00136     req->sector = bh->b_blocknr<<1;  
  50. 00137     req->nr_sectors = 2;  
  51. 00138     req->buffer = bh->b_data;  
  52. 00139     req->waiting = NULL;  
  53. 00140     req->bh = bh;  
  54. 00141     req->next = NULL;  
  55. 00142     add_request(major+blk_dev,req);  
  56. 00143 }  


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00040 #define IN_ORDER(s1,s2) \  
  2. 00041 ((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \  
  3. 00042 ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \  
  4. 00043 (s1)->sector < (s2)->sector))))  


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 00064 static void add_request(struct blk_dev_struct * dev, struct request * req)  
  2. 00065 {  
  3. 00066     struct request * tmp;  
  4. 00067   
  5. 00068     req->next = NULL;  
  6. 00069     cli();  
  7. 00070     if (req->bh)  
  8. 00071         req->bh->b_dirt = 0;  
  9. 00072     if (!(tmp = dev->current_request)) {  
  10. 00073         dev->current_request = req;  
  11. 00074         sti();  
  12. 00075         (dev->request_fn)();  
  13. 00076         return;  
  14. 00077     }  
  15. 00078     for ( ; tmp->next ; tmp=tmp->next)  
  16. 00079         if ((IN_ORDER(tmp,req) ||   
  17. 00080             !IN_ORDER(tmp,tmp->next)) &&  
  18. 00081             IN_ORDER(req,tmp->next))  
  19. 00082             break;  
  20. 00083     req->next=tmp->next;  
  21. 00084     tmp->next=req;  
  22. 00085     sti();  
  23. 00086 }  

        以上代码列出了从file_read函数到add_request的过程,其中add_request使用了电梯算法来添加请求到等待队列。这里关键是分析电梯算法按一个什么样的逻辑插入到等待队列当中。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #define IN_ORDER(s1,s2) \  
  2. ((s1)->cmd<(s2)->cmd || (s1)->cmd==(s2)->cmd && \  
  3. ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \  
  4. (s1)->sector < (s2)->sector)))  
         先来了解一下request项的关键结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct request {  
  2.     int dev;        /* -1 if no request */  
  3.     int cmd;        /* READ or WRITE */  
  4.     int errors;  
  5.     unsigned long sector;  
  6.     unsigned long nr_sectors;  
  7.     char * buffer;  
  8.     struct task_struct * waiting;  
  9.     struct buffer_head * bh;  
  10.     struct request * next;  
  11. };  
        dev是设备号,根据linux0.11代码里面涉及的每个存储设备编了号;cmd是指这个请求的命令,也就是读或写,其中定义READ=0,WRITE=1;sector是扇区号;buffer是读或写的缓冲区;waiting这个task_struct结构是当前进程等待队列指针;next是指向下一请求的指针。另外的errors,nr_sectors,bh我们目前不关注。

        用结构更清晰的代码开始分析IN_ORDER的原理:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool inorder(request &s1,request &s2)  
  2. {  
  3.     if (s1.cmd<s2.cmd)  
  4.     {  
  5.         return true;//only when s1.cmd = READ; s2.cmd = WRITE;  
  6.     }  
  7.     else if ( s1.cmd == s2.cmd )  
  8.     {  
  9.         if (s1.dev < s2.dev)  
  10.         {  
  11.             return true;// when (s1.cmd==s2.cmd) && (s1.dev<s2.dev)  
  12.         }  
  13.         else if ( s1.dev == s2.dev )  
  14.         {  
  15.             if (s1.sector<s2.sector)  
  16.             {  
  17.                 return true;// when when (s1.cmd==s2.cmd) && (s1.sector<s2.sector)  
  18.             }  
  19.             return false;// when when (s1.cmd==s2.cmd) && (s1.sector>=s2.sector)  
  20.         }  
  21.         return false;// when (s1.cmd==s2.cmd) && (s1.dev>s2.dev)  
  22.     }  
  23.     return false;// when s1.cmd>s2.cmd  
  24. }  
        从上面的每个函数返回地方的注释看出返回时的比较状态。用自然语言描述一下:IN_ORDER返回s1与s2的优先级比较结果,s1比s2高的话,返回true;s1不高于s2的话,返回false;

        IN_ORDER的内部比较思想是:先比较操作类型,读操作优先于写操作;如果操作类型相同,则比较设备号,设备号小的设备优先于设备号大的;如果设备号也相同,则比较扇区号,先处理扇区号小的扇区,意思就是从磁头从里向外读写了;如果扇区号也相同,那么返回false,也就是前面的s1优先级低于后面的s2。

      然后我们就可以来分析插入的代码了:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (!(tmp = dev->current_request)) {  
  2.     dev->current_request = req;  
  3.     sti();  
  4.     (dev->request_fn)();  
  5.     return;  
  6. }  
        上面代码的意思是:如果设备的当前处理请求项为空,那么把需要处理的请求项设置为当前处理项,当立即调用处理请求函数dev->request_fn()。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for ( ; tmp->next ; tmp=tmp->next)  
  2.     if ((IN_ORDER(tmp,req) ||  
  3.         !IN_ORDER(tmp,tmp->next)) &&  
  4.         IN_ORDER(req,tmp->next))  
  5.         break;  
  6. req->next=tmp->next;  
  7. tmp->next=req;  
      上面的代码是在设备的当前请求项tmp不为空(也就是设备在忙)的情况下使用。我们可以dev->current_request理解为等待队列的一个头节点;这个头指点指向一个等待队列;我们需要把请求按一定的方法(也就是IN_ORDER算法)插入到等待队列中。

        首先,req是待插入节点,我们以tmp为哨兵节点,IN_ORDER(tmp,req),是比较哨兵节点是否比待插入节点优先级高。

        我们作一个约定,a比b优先级高的话使用a>b的记号,否则是a<=b。注意=的临界状态,不少人因为临界状态理解不好影响整个插入算法的理解。
把上面的代码做一个伪代码来理解:

for( ; tmp->next; tmp=tmp->next)

{

if ( tmp>req && req>tmp->next) break;//也就是哨兵节点比待插入节点优先级要高,并且待插入节点的优先级要比哨兵后的一个节点要高,那么应该插入到tmp与tmp->next之间。

else if ( tmp <= tmp->next  && req>tmp->next ) break;//也就是哨兵节点不比它后面的节点优先级高——我们知道这是一个已经排序的队列,tmp优先级肯定大于或等于tmp->next,这里出现tmp <= tmp->nex成立的唯一情况就是tmp=tmp->next,也就是两个节点的优先级一样高。那么这个时候req>tmp->next也一定不会成立,就会继续下一个循环;等等!我们漏掉了一个情况——当tmp是current_request的时候,有可能出现tmp<=tmp->next的情况,此时如果req>tmp->next也成立,那么就会插入current_request后面,也就是tmp的后面!这种情况必须要理解!

else continue;//如果不适合插入tmp后面,继续下一个循环;

}

所以上面的总体意思就是:优先级从大到小排列,同一优先级的话,插入这一系统相同优先级的节点的最后面。可以说的细一点,先处理读请求,再处理写请求;同一读或写请求先处理设备号小的设备请求,再处理设备号大的设备请求,同一读或写请求,同一设备,按先里面的扇区再到外面的扇区的顺序处理。


为了更有说服力,以下我按电梯算法编写了代码,可以修改注掉的inorder部分再做测试。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. #define READ 0  
  5. #define WRITE 1  
  6.   
  7. struct request {  
  8.     int dev;        /* -1 if no request */  
  9.     int cmd;        /* READ or WRITE */  
  10.     int errors;  
  11.     unsigned long sector;  
  12.     unsigned long nr_sectors;  
  13.     char * buffer;  
  14.     //struct task_struct * waiting;  
  15.     //struct buffer_head * bh;  
  16.     struct request * next;  
  17. };  
  18.   
  19. #define IN_ORDER(s1,s2) \  
  20.     ((s1)->cmd<(s2)->cmd || (s1)->cmd==(s2)->cmd && \  
  21.     ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \  
  22.     (s1)->sector < (s2)->sector)))  
  23.   
  24. // 作为解析,以明白的分支结构重写一个内容一样的inorder函数  
  25. bool inorder(struct request *s1,struct request *s2)  
  26. {  
  27.     if (s1->cmd<s2->cmd)  
  28.     {  
  29.         return true;//only when s1->cmd = READ; s2->cmd = WRITE;  
  30.     }  
  31.     else if ( s1->cmd == s2->cmd )  
  32.     {  
  33.         if (s1->dev < s2->dev)  
  34.         {  
  35.             return true;// when (s1->cmd==s2->cmd) && (s1->dev<s2->dev)  
  36.         }  
  37.         else if ( s1->dev == s2->dev )  
  38.         {  
  39.             if (s1->sector<s2->sector)  
  40.             {  
  41.                 return true;// when when (s1->cmd==s2->cmd) && (s1->sector<s2->sector)  
  42.             }  
  43.             return false;// when when (s1->cmd==s2->cmd) && (s1->sector>=s1->sector)  
  44.         }  
  45.         return false;// when (s1->cmd==s2->cmd) && (s1->dev>s2->dev)  
  46.     }  
  47.     return false;// when s1->cmd>s2->cmd  
  48. }  
  49.   
  50. void AddRequest(struct request * &head,struct request *req)  
  51. {  
  52.     if (!head)  
  53.     {  
  54.         head = req;  
  55.         head->next = 0;  
  56.         return ;  
  57.     }  
  58.     struct request * tmp = head;  
  59.     for (;tmp->next;tmp = tmp->next)  
  60.     {  
  61.         // 使用inorder和宏IN_ORDER是一样的结果  
  62.         //if ( ( inorder(tmp,req)||  
  63.         //      !inorder(tmp,tmp->next)) &&  
  64.         //      inorder(req,tmp->next))  
  65.         if ( ( IN_ORDER(tmp,req)||  
  66.             !IN_ORDER(tmp,tmp->next)) &&  
  67.             IN_ORDER(req,tmp->next))  
  68.         {  
  69.             break;  
  70.         }  
  71.     }  
  72.     req->next = tmp->next;  
  73.     tmp->next = req;  
  74.     return;  
  75. }  
  76.   
  77. void PrintQueen(struct request * n)  
  78. {  
  79.     while (n)  
  80.     {  
  81.         printf("(%d,%d,%d),",n->cmd,n->dev,n->sector);  
  82.         n = n->next;  
  83.     }  
  84.     printf("\n");  
  85. }  
  86. int main(int argc,char ** argv)  
  87. {  
  88.     struct request s1;  
  89.     struct request * pHead = 0;  
  90.   
  91.     for (int i = 0;i<100;i++)  
  92.     {  
  93.         struct request *req = new struct request;  
  94.         req->cmd = rand()%2;  
  95.         req->dev =  rand()%10;  
  96.         req->sector =  rand()%100;  
  97.         AddRequest(pHead,req);  
  98.         PrintQueen(pHead);  
  99.     }  
  100.     return 0;  
  101. }  

大家看结果,红色的是新插入的节点,和我上述的结果是一致的。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值