flashsim源码阅读-3

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

开始介绍intq的相关函数:

disksim_dumpintq () 函数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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 很像,先看其函数原代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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;
}
}
}

代码段第一段:

1
2
3
4
5
6
7
8
#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的时间。这是由试验和错误,可能需要一些调整。
代码段第二段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
   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结构体的定义:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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>

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

read

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//如果队列为空,直接将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 ()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 ()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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表示没有找到,其实就是双链表删除操作|

热评文章