SSDsim之initiation
本文继续介绍SSDsim中有关的代码解析和注解:
initiation()函数的主要功能
SSDsim是一个利用仿真trace文件进行过程模拟的软件,所以对于SSDsim来说,重要的要有三个输入文件可以提供给sim进行IOtrace的信息以及相关的初始化信息,这三个文件主要分别是:
参数文件 ——>负责提供模拟的SSD的相关参数因子;
负载踪迹文件trace——>负责提供IOtrace的输入数据;
输出文件——>负责提供一个文件供SSDsim模拟完成后输出统计数据;
初始化函数initiation的功能主要是为SSD的结构体分配内存空间,其中SSD的结构体是一个极大的叠加性结构体,在这个ssd_info类型的结构体中内嵌了各个SSD闪存不同层级结构的结构体,也就是分配这个SSD结构体,同时也对每一个channel,每一个channel下的chip,每一个chip下的die,每一个die下的plane,每一个plane下的block,每一个block下的page结构体都进行了相应的初始化处理。每一个层级的结构都有对应的结构体信息,初始化过程就是利用参数文件对这些结构体进行参数的赋值。
而负责存放输入数据的参数文件也有对应的一个结构体定义parameter_value,这个结构体负责从参数文件中利用fget()函数进行参数格式数据的逐行读取和保存,参数文件说明当前实验所模拟固态盘的硬件结构,闪存转换层算法及数据缓存算法。参数文件的第一个部分包括两种类型的参数,硬件的组织结构(芯片内的晶圆数,每个晶圆中分组的数量,每个分组内的块数量等),及时间参数;第二个部分包括两种类型的参数,能耗参数和闪存转换层配置参数。
负载踪迹文件既通常所说的trace文件,用作记录特定的负载的所有的请求的到达时间,请求的类型,请求的逻辑地址,请求的大小。负载踪迹文件的所有请求都是按照时间顺序排列。SSDsim根据这个踪迹文件,可以重现出这个特定负载或者应用环境下的请求流,通过参数文件设定的硬件结构和算法,可以得到在这种负载下该硬件结构和算法的性能,从而寻找出在这个负载下最优的硬件结构和或算法。
输出文件是用来记录每个请求的响应时间,以及特定硬件结构和软件算法在服务这个负载时的性能,能耗统计结果。每个请求的队列等待时间,服务时间和响应时间都依次输出到输出文件中,在模拟完成后输出服务整个负载的平均响应时间,总能耗,读写请求数等统计信息。
initiation函数的调用关系图
首先看一下关于函数的调用关系图
我们从图中可以看到,这个initiation函数主要是调用了load_parameters函数进行参数文件的读取,该函数完成后则参数结构体paramenter就完成了初始化,随后进行了dram的初始化,之后就是channel的初始化,而channel的初始化随之又会初始化chip,依次反复,直到所有层级的结构都初始化完毕。最后对输出文件进行初始化,主要是将一些默认缺省的信息写入到输出文件中,而后刷新缓冲区输出到显示设备中。
initiation函数的控制流图
initiation函数的控制流图如下:
我们从控制流图可以详细直观的可以看到这个函数的过程,结合代码进行分析会有很大的便利性,
同时我们给出关于initiation函数的关系流图:
源代码和相关注解
struct ssd_info initiation(struct ssd_info *ssd)
//初始化模拟的ssd结构所需的参数
{ //SSD->channel->chip->die->plane->block->page->subpage
unsigned int x=0,y=0,i=0,j=0,k=0,l=0,m=0,n=0;
//这里定义的八个uint变量应该就是指为了循环遍历初始化上述这八个层次中的所有结构参数而设定的
errno_t err; //errno_t类型是用于条件判断的
char buffer[300]; //缓冲区初始化,大小为300Byte
struct parameter_value *parameters; //声明输入参数文件
//指向文件指针的一个指针,FILE是C语言stdio.h头文件中定义的一个结构体类型,该结构体包含了与文件相关的一系列成员参数,如文件属性等。
printf("input parameter file name:");
gets(ssd->parameterfilename);
printf("\ninput trace file name:");
gets(ssd->tracefilename);
printf("\ninput output file name:");
gets(ssd->outputfilename);
printf("\ninput statistic file name:");
gets(ssd->statisticfilename);
//导入ssd的配置文件
parameters=load_parameters(ssd->parameterfilename);
//将ssd结构体的参数导入至定义的参数文件中,而后赋值给参数结构体变量
ssd->parameter=parameters;
//直接将保存参数的结构体变量进行赋值
//计算ssd中每个通道channel中的的闪存页总数量
ssd->page=ssd->parameter->chip_num*ssd->parameter->die_chip*ssd->parameter->plane_die*ssd->parameter->block_plane*ssd->parameter->page_block;
//初始化 dram
ssd->dram = (struct dram_info *)malloc(sizeof(struct dram_info)); //分配dram内存空间
alloc_assert(ssd->dram,"ssd->dram"); //判断分配情况
memset(ssd->dram,0,sizeof(struct dram_info)); //初始化dram的空间->清零
/*
初始化dram,这里会根据参数结构体中dram的相关参数对dram结构进行初始化,主要的工作是:
dram_info结构体初始化以及设置dram容量、设置dram中的buffer缓冲区、初始化映射表
*/
initialize_dram(ssd);
//初始化通道
ssd->channel_head=(struct channel_info*)malloc(ssd->parameter->channel_number * sizeof(struct channel_info));
alloc_assert(ssd->channel_head,"ssd->channel_head");
//分配通道并且检查内存分配情况
//初始化清零内存
initialize_channels(ssd);
//初始化通道函数,主要根据参数文件的SSD通道数对每一个通道进行初始化操作设置
printf("\n");
if((err=fopen_s(&ssd->outputfile,ssd->outputfilename,"w")) != 0) //若打开输出结果文件失败则打印错误信息并且返回空指针
{
//w参数即指以空文件的方式打开,相当于创建一个新文件,若存在同名文件则该同名文件内数据会被销毁
printf("the output file can't open\n");
return NULL;
}
printf("\n");
if((err=fopen_s(&ssd->statisticfile,ssd->statisticfilename,"w"))!=0)
//若打开statisticfile文件失败则打印错误信息并且返回空指针
{
printf("the statistic file can't open\n");
//statisticfile文件是一个负责统计相关信息的文件
return NULL;
}
printf("\n");
//以下是分别按照中间的固定格式向指定文件中写入相关的文件名信息,比如第一个就是按照parameter file:***这样的格式写入文件中,其他类似
fprintf(ssd->outputfile,"parameter file: %s\n",ssd->parameterfilename); //将参数文件名写入到outputfile中
fprintf(ssd->outputfile,"trace file: %s\n",ssd->tracefilename); //将负载踪迹文件名写入到outputfile中
fprintf(ssd->statisticfile,"parameter file: %s\n",ssd->parameterfilename); //将参数文件名写入到统计文件中
fprintf(ssd->statisticfile,"trace file: %s\n",ssd->tracefilename); //将负载踪迹文件名写入到统计文件中
/*
*fflush()函数:负责冲洗流中的信息,也就是清除读写缓冲区中的内容,需要立即把输出缓冲区的数据进行物理写入时;成功刷新则返回0
*函数原型是: int fflush(FILE *stream)
*fflush(stdin):刷新标准输入缓冲区(一般指键盘),把输入缓冲区内的东西丢弃;
*fflush(stdout):刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上;
*******************************************************************************************************
*如果stream指向输出流或者更新流,并且这个更新流最近执行的操作不是输入,那么flush函数将把任何未被写入的数据写入stream指向的文件
*否则,flush函数的行为是不确定的;fflush(NULL)清空所有输出流和更新流,如果发生写错误,flush函数会给那些流打上错误标记并且返回EOF,否则返回0
*所以,如果stream指向输入流如stdin则fflush函数的行为是不确定的。此时使用fflush(stdin)是不正确的
*/
fflush(ssd->outputfile); //刷新outputfile文件中的数据并且将其打印至标准输出设备上
fflush(ssd->statisticfile); //刷新statisticfile文件中的数据并且将其打印至标准输出设备上
if((err=fopen_s(&fp,ssd->parameterfilename,"r"))!=0) //以只读的方式打开参数文件,此时fp指针指向了参数文件
{
printf("\nthe parameter file can't open!\n");
return NULL;
}
//fp=fopen(ssd->parameterfilename,"r");
fprintf(ssd->outputfile,"-----------------------parameter file----------------------\n");
//向outputfile文件写入格式字符串
//同上
while(fgets(buffer,300,fp))
//这里的buffer是SSD的buffer,而并不是在初始化时使用到的buf,初始化时的buf在函数返回时便已被自动回收了
{
//逐行读取参数文件中的数据到ssd缓冲区buffer中,每次读取一行300字节后会执行循环体内的代码部分
fprintf(ssd->outputfile,"%s",buffer);
//将每一行缓冲区的数据流以字符串格式写入到outputfile文件中
fflush(ssd->outputfile);
//刷新outputfile文件中的数据并将其打印至标准输出设备上
fprintf(ssd->statisticfile,"%s",buffer);
//将每一行缓冲区的数据流以字符串格式写入到statisfile中
fflush(ssd->statisticfile);
//刷新outputfile文件中的数据并将其打印至标准输出设备上
}
fprintf(ssd->outputfile,"\n");
fprintf(ssd->outputfile,"-----------------------simulation output----------------------\n");
fflush(ssd->outputfile);
fprintf(ssd->statisticfile,"\n");
fprintf(ssd->statisticfile,"-----------------------simulation output----------------------\n");
fflush(ssd->statisticfile);
fclose(fp); //关闭文件流
printf("initiation is completed!\n");
return ssd;
}