flashsim源码阅读(intq队列操作)7-8

来源:互联网 发布:淘宝店免费开店要钱吗 编辑:程序博客网 时间:2024/06/03 22:06

推荐源码阅读的的工具是sourceInsight3.5,直接将源码文件导入,就会生成一个各文件关联的工程,这个工具可自行百度,配合Nodepad++修改注释阅读更好,因为SI不知道为什么对中文注释输入的支持不是很好。继续昨天的源码阅读。
昨天研究到disksim.c中的extarq队列的操作函数,extraq队列就像是预先申请的空的内存,今天看的是intq队列操作函数,intq是处理调度event的队列。
开始介绍intq的相关函数:

disksim_dumpintq () 函数

先给出该函数在disksim.c中的的原代码段:

static void disksim_dumpintq (){   event *tmp;   int i = 0;   tmp = disksim->intq;   while (tmp != NULL) {      i++;      fprintf (outputfile, "time %f, type %d\n",            tmp->time, tmp->type);      tmp = tmp->next;   }}

disksim->inqdisksim->extraq一样都是指向队列的头部元素。这个函数就是遍历intq队列,将intq队列里面每个event的信息(队列中的event的time,Type)输出到outputfile中去,可能是为了之后的调试使用的函数。

INLINE void addtointq (event *newint) 函数

一看这个函数也是一个内联函数,和之前的addtoextraq 很像,先看其函数原代码段:

INLINE void addtointq (event *newint){  /* WARNING -- HORRIBLE HACK BELOW   * In order to make the Memulator run (which sometimes has events arrive   * "late") I've (JLG) commented out the following snippet.  A better   * solution should be arrived at...   */#if 0   if ((temp->time + DISKSIM_TIME_THRESHOLD) < simtime) {       //如果当前的event的时间比simtime还要早,就是已经错过了调度      fprintf(stderr, "Attempting to addtointq an event whose time has passed\n");      fprintf(stderr, "simtime %f, curr->time %f, type = %d\n", simtime, temp->time, temp->type);      exit(1);   }#endif   switch(disksim->trace_mode) {   //这边关于disksim->trace_mode指的是负载还是?有待考证   case DISKSIM_MASTER:   //这个Master模式下把,新加入的newint写入到tracepipes[1]中     if(write(disksim->tracepipes[1], (char *)newint, sizeof(event)) <= 0)       {     //  printf("addtointq() -- master write fd = %d\n", disksim->tracepipes[1]);     //  perror("addtointq() -- master write");       }     break;   case DISKSIM_SLAVE:   //这个SLAVE模式下把tracepipes[0]中的内容传到tmpevt(event类型)中去     {       event tmpevt;       double timediff;       //       printf("addtointq() -- slave read\n");       ddbg_assert(read(disksim->tracepipes[0], (char *)&tmpevt, sizeof(event)) > 0);       timediff = fabs(tmpevt.time - newint->time);//计算tmpevt和newint的时间差/*         printf("remote event: %d %f, local event: %d %f\n", *//*            tmpevt.type,  *//*            newint->type,  *//*            newint->time); */       ddbg_assert(tmpevt.type == newint->type);       //       ddbg_assert(timediff <= 0.001);       newint->time = tmpevt.time;       if(timediff > 0.000001) {     printf("*** SLAVE: addtointq() timediff = %f\n", timediff);       }       fflush(stdout);     }     break;   case DISKSIM_NONE:   default:     break;   }    //如果队列为空,直接将newint加入到intq队列中   if (disksim->intq == NULL) {      disksim->intq = newint;      newint->next = NULL;      newint->prev = NULL;   }    //队列非空,比较newint时间和intq当前头部event的时间   //如果newint时间小,则直接将其插入到intq队列的头部   else if (newint->time < disksim->intq->time) {      newint->next = disksim->intq;      disksim->intq->prev = newint;      disksim->intq = newint;      newint->prev = NULL;   } else {       //否则寻找intq队列中前一项event时间比newint小的位置插入       //也就是说intq队列中的event是按照time依次递增排序的      event *run = disksim->intq;      assert(run->next != run);      while (run->next != NULL) {         if (newint->time < run->next->time) {            break;         }         run = run->next;      }      newint->next = run->next;      run->next = newint;      newint->prev = run;      if (newint->next != NULL) {         newint->next->prev = newint;      }   }}

代码段第一段:

#if 0   if ((temp->time + DISKSIM_TIME_THRESHOLD) < simtime) {       //如果当前的event的时间比simtime还要早,就是已经错过了调度      fprintf(stderr, "Attempting to addtointq an event whose time has passed\n");      fprintf(stderr, "simtime %f, curr->time %f, type = %d\n", simtime, temp->time, temp->type);      exit(1);   }#endif

这是个宏定义主要是为了处理当调度的当前event时间过了系统时间不能被服务的,即不能加入intq队列里面的一个操作,看下#define DISKSIM_TIME_THRESHOLD 0.0013这个参数在disksim_global.h中定义,这个阈值允许disksim的一些区别内部时间和simos的时间。这是由试验和错误,可能需要一些调整。
代码段第二段:

   switch(disksim->trace_mode) {   case DISKSIM_MASTER:     if(write(disksim->tracepipes[1], (char *)newint, sizeof(event)) <= 0)       {     //  printf("addtointq() -- master write fd = %d\n", disksim->tracepipes[1]);     //  perror("addtointq() -- master write");       }     break;   case DISKSIM_SLAVE:     {       event tmpevt;       double timediff;       //       printf("addtointq() -- slave read\n");       ddbg_assert(read(disksim->tracepipes[0], (char *)&tmpevt, sizeof(event)) > 0);       timediff = fabs(tmpevt.time - newint->time);/*         printf("remote event: %d %f, local event: %d %f\n", *//*            tmpevt.type,  *//*            tmpevt.time, *//*            newint->type,  *//*            newint->time); */       ddbg_assert(tmpevt.type == newint->type);       //       ddbg_assert(timediff <= 0.001);       newint->time = tmpevt.time;       if(timediff > 0.000001) {     printf("*** SLAVE: addtointq() timediff = %f\n", timediff);       }       fflush(stdout);     }     break;   case DISKSIM_NONE:   default:     break;   }

这里的disksim->trace_mode的有关参数在disksim结构体的定义:

int tracepipes[2];      enum { DISKSIM_MASTER, DISKSIM_SLAVE, DISKSIM_NONE } trace_mode;

最早看到trace_mode的调用是在之前的disksim_simulate_event相关的代码段:

if ((curr = getnextevent()) == NULL) {    disksim_simstop ();  }   else {    switch(disksim->trace_mode) {    case DISKSIM_NONE:    case DISKSIM_MASTER://      fprintf(outputfile, "*** DEBUG TRACE\t%f\t%d\t%d\n",   //        simtime, curr->type, num);     //    fflush(outputfile);          break;    case DISKSIM_SLAVE:      break;    }

disksim_simulate_event 只是调试用的语句,但在addtointq 函数得仔细看看。首先介绍C里面的两个函数write,read函数。
write:
write函数的定义在C的 头文件:<unistd.h>

ssize_t write(int fd, const void *buf, size_t nbyte);fd:文件描述符;buf:指定的缓冲区,即指针,指向一段内存单元;nbyte:要写入文件指定的字节数;返回值:写入文档的字节数(成功);-1(出错)write函数把buf中nbyte写入文件描述符handle所指的文档,成功时返回写的字节数,错误时返回-1.

read

ssize_t read(int fd, void * buf, size_t count);定义函数:ssize_t read(int fd, void * buf, size_t count);函数说明:read()会把参数fd 所指的文件传送count 个字节到buf 指针所指的内存中. 若参数count0, 则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动.

理解这两个函数以后,再看上面的trace_mode可以发现,mode其实分为主动(Master),从动(Slave),另一个(None)其实什么也不做。
主动模式下就是将(函数入口参数)newint指针指向的内容写入到tracepipes[1]中去,从动模式下将把tracepipes[0]中的内容传到tmpevt(event类型)中去,并比较了读到内容(存在tmpevt中)和newint指向内容的时间差,请求类型(type)。
函数最后面的代码

//如果队列为空,直接将newint加入到intq队列中   if (disksim->intq == NULL) {      disksim->intq = newint;      newint->next = NULL;      newint->prev = NULL;   }    //队列非空,比较newint时间和intq当前头部event的时间   //如果newint时间小,则直接将其插入到intq队列的头部   else if (newint->time < disksim->intq->time) {      newint->next = disksim->intq;      disksim->intq->prev = newint;      disksim->intq = newint;      newint->prev = NULL;   } else {       //否则寻找intq队列中前一项event时间比newint小的位置插入       //也就是说intq队列中的event是按照time依次递增排序的      event *run = disksim->intq;      assert(run->next != run);      while (run->next != NULL) {         if (newint->time < run->next->time) {            break;         }         run = run->next;      }      newint->next = run->next;      run->next = newint;      newint->prev = run;      if (newint->next != NULL) {         newint->next->prev = newint;      }   }

其实就是依据newint指针指向的event的时间大小插入到intq队列中去,intq队列中的event是按时间大小从小到大依次排序的。从插入过程可以理解,inqt和extraq是不一样的,是个双链表。
总结addtointq()函数是将一个event加入到intq队列中去,time表示event发生时间,intq队列中的event是按时间的升序排列的函数输出参数是新加入的event的指针。
这里写图片描述

getfromintq ()函数:

INLINE static event * getfromintq (){   event *temp = NULL;   if (disksim->intq == NULL) {      return(NULL);   }   temp = disksim->intq;   disksim->intq = disksim->intq->next;   if (disksim->intq != NULL) {      disksim->intq->prev = NULL;   }   temp->next = NULL;   temp->prev = NULL;   return(temp);}

该函数从intq头部取出下一次要调度服务的event,(get-from-intq),disksim->intq指向队列的下一个位置的event,并且切断对上一个event指向,相当于从intq中移除该event,函数返回的是event 指针。

removefromintq ()函数

INLINE int removefromintq (event *curr){   event *tmp;   tmp = disksim->intq;   while (tmp != NULL) {      if (tmp == curr) {         break;      }      tmp = tmp->next;   }   if (tmp == NULL) {      return(FALSE);   }   if (curr->next != NULL) {      curr->next->prev = curr->prev;   }   if (curr->prev == NULL) {      disksim->intq = curr->next;   } else {      curr->prev->next = curr->next;   }   curr->next = NULL;   curr->prev = NULL;   return(TRUE);}

从intq(调度表)队列中移除指定的event项,函数返回Ture表示找到该项event,FALSE表示没有找到,其实就是双链表删除操作。

对intq(event调度队列)的函数总结

函数名 输入参数 返回类型 完成操作 disksim_dumpintq () void 遍历intq队列,将intq队列里面每个event的信息(队列中的event的time,Type)输出到outputfile中去,可能是为了之后的调试使用的函数。 addtointq () event *newint void 将一个event加入到intq队列中去,time表示event发生时间,intq队列中的event是按时间的升序排列的函数输出参数是新加入的event的指针。 getfromintq () event类型的指针 从intq头部取出下一次要调度服务的event,disksim->intq指向队列的下一个位置的event,并且切断对上一个event指向,相当于从intq中移除该event,函数返回的是event 指针。 removefromintq() event *curr int 从intq队列中移除指定的event项,函数返回Ture表示找到该项event,FALSE表示没有找到,其实就是双链表删除操作
原创粉丝点击