这篇博文将继续分析SSDsim的初始化过程,主要的分析对象函数是这个load_parameters()函数,接下来让我们分析下这个函数的主要框架以及给出相应的源码注释。
load_parameters函数的主要功能是负责将参数文件parameter中的SSD相关设置参数一一传输至ssd->parameters这个结构体定义中。其中,load_parameters函数接收了一个参数文件名的函数参数,然后在函数内部申请了一部分缓冲区buf作为数据缓存处理的区域。而后通过fget()函数打开parameter_file文件并且在buf中逐一逐行进行数据的读取和相应的保存,由于参数文件中关于SSD参数的格式都规定为:* = *这样的固定格式,所以通过这种逐行读取可以实现对特定字符串参数的读取从而设置ssd->parameters中的相关参数。对于各个参数的判断该函数使用了一个多分支if-else的判断结构,每一次fget()一行数据后都会截取相应的字符串数据然后在这个if判断结构中进行验证和设置。
当文件读取完毕后,函数便开始清理buf缓冲区并且关闭相关的打开文件指针。
struct parameter_value *load_parameters(char parameter_file[30])
{
FILE * fp; //fp相当于指向某一个文件的一个指针,FILE类型是stdio头文件的一种结构体变量,代表指向文件的类型
FILE * fp1;
FILE * fp2;
errno_t ferr; //errno_t类型可以简单理解为int类型,用于判断打开文件的判定条件
struct parameter_value *p;
char buf[BUFSIZE]; //初始化用到的缓存区
int i;
int pre_eql,next_eql; //pre_eql变量是代表了buf缓冲区内=之前的其他字符数量,相当于位置;next_eql变量则代表了=后下一个字符的位置
int res_eql; //用于在参数检查中作判断之用,利用了strcmp函数返回的值判断参数是否正确
char *ptr; //ptr负责记录 = 出现的位置
p = (struct parameter_value *)malloc(sizeof(struct parameter_value)); //动态分配并初始化p的内存空间
alloc_assert(p,"parameter_value");
memset(p,0,sizeof(struct parameter_value)); //清零
p->queue_length=5; //请求队列最长为5
memset(buf,0,BUFSIZE); //缓冲区内存清零
/************************************************************************************************
关于fopen_s函数的原型:
errno_t fopen_s( FILE** pFile, const char *filename, const char *mode );
这个函数接收的第一个函数参数了代表了打开的文件指针指向的地址(由于是指针的指针,所以这个文件指针地址
是指向了一个指针也就是一个特定的地址),文件名,以及访问的类型;如果成功返回0,失败则返回相应的错误代码。
主要的访问类型mode属性有:
"r":打开以进行读取。如果该文件不存在,或无法找到,fopen_s调用失败。
"w":打开一个空文件以进行写入。如果该文件存在,其内容将被销毁。
下面分别用了三个FILE文件指针打开了参数文件并且实现了指向了三个文件,分别是参数文件、参数名文件、参数值文件
************************************************************************************************/
if((ferr = fopen_s(&fp,parameter_file,"r"))!= 0) //fopen_s函数作用是读取parameter_file并且让fp指向它,r是代表只读文件,结果正确则返回一个0
{
//如果打开失败;(原因可能找不到这个文件或者文件不存在)
printf("the file parameter_file error!\n");
return p;
}
if((ferr = fopen_s(&fp1,"parameters_name.txt","w"))!= 0) //之前定义的3个文件指针在此处都用到了,作用就是将相应的文件指针分别指向三个文件流,第一个是只读,第二和第三是可写,即fp只需读数据,而fp2和fp3需要写入数据
{ //打开一个内容为空的参数名文件并且将fp1文件指针指向的地址内容写入到这个文件中
printf("the file parameter_name error!\n"); //如果打开失败则返回并显示错误信息
return p;
}
if((ferr = fopen_s(&fp2,"parameters_value.txt","w")) != 0)
{
printf("the file parameter_value error!\n"); //打开一个内容为空的参数值文件并且将fp2文件指针指向的地址内容写入到这个文件中
return p;
}
/************************************************************************************************
关于fgets()函数的原理:
函数原型:char *fgets(char *buf, int bufsize, FILE *stream);
参数
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明存储数据的大小。
*stream: 文件结构体指针,将要读取的文件流。
从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋'\0'),
如果文件中的该行,不足bufsize个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行,
但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。函数成功将返回buf,失败或读到文件结尾返回NULL。因此我们不能直接通过fgets
的返回值来判断函数是否是出错而终止的,应该借助feof函数或者ferror函数来判断。
stream文件流指针体指向文件内容地址的偏移原则
如果使用fgets()读取某个文件,第一次读取的bufsize为5,而文件的第一行有10个字符(算上'\n'),那么读取文件的指针会偏移至当前读取完的这个字符
之后的位置。也就是第二次再用fgets()读取文件的时候,则会继续读取其后的字符。而如果使用fgets() 读取文件的时候bufsize大于该行的字符总数加2
(多出来的两个,一个保存文件本身的'\n'换行,一个保存字符串本身的结束标识'\0'),文件并不会继续读下去,仅仅只是这一行读取完,随后指向文件的指针
会自动偏移至下一行。
************************************************************************************************/
/*
fgets函数是每次按行读取的,如果bufsize大于当前行的字符数量,那么将逐一读取所有行的数据,所以这里可以用fgets函数的返回值作为while循环的判断条件,这里fgets会读取200个字节大小的
内容到buf缓冲区中,但是如果fp指向的文本文件(ssdsim中的参数文件就是以txt文本保存的)按照分行参数设置的规则的话,那这里fgets读完一行内容后
就会继续读下一行的内容,每次都会将当前行中的200字节(如果有200字节长度)内容都读到buf中。
如果bufsize小于当前行的字符数量,那么这个函数会读取bufsize-1个字符到buf中,此时读取文件的指针会自动指向了第bufsize个字符的位置也就是当前
读取完毕的最后一个字符之后的那个位置。
*/
while(fgets(buf,200,fp)){ //fgets()函数的作用是将fp指向的文件内容读取200个字节的数据到buf数组中,返回值是char buf[0]
if(buf[0] =='#' || buf[0] == ' ') continue; //如果参数文件的第一个数据是#或者是空字符则直接结束本次循环,执行下一步循环
ptr=strchr(buf,'='); //strchr函数的作用是查找第一次出现'='的buf位置,返回值是char*即首次出现=的位置
if(!ptr) continue; //此处之所以要判断=处的位置,初步猜测是由于参数文件内相关的参数文件是以"chip number=**"这样的格式存放的,所以要一一判断出所有的参数
pre_eql = ptr - buf; //pre_eql变量是代表了buf缓冲区内=之前的其他字符数量,相当于位置
next_eql = pre_eql + 1; //next_eql变量则代表了=后下一个字符的位置
while(buf[pre_eql-1] == ' ') pre_eql--; //若=前面的位置字符是空格,则一直向前推进直到pre_eql所指的位置字符非空格,这种方式避免了诸如 chip number = **这样的参数所带来的处理问题
buf[pre_eql] = 0; //令=前面的非空格字符的后一个字符为0,如果=之前没有空格则是将=赋值为0
//之所以这里要将非空格字符后赋值为0是因为在下面的strcmp字符串比较函数中,strcmp只有在字符串中遇到\0才会停止比较
/**************************************************************************************************************
下面的if多分支结构的作用是负责在buf缓冲区中逐一读取到相关参数值并且将"="后面相关的参数值读入到ssd中(这里的ssd就是指p所指的成员);
所以这里的整个if多分支结构就实现了在while循环中反复读取参数到ssd中
sscanf函数的相关原型:
int sscanf(const char *buffer,const char *format,[argument ]...);
buffer:存储的数据
format格式控制字符串
argument 选择性设定字符串
sscanf会从buffer里读进数据,依照format的格式将数据写入到argument里。
**************************************************************************************************************/
//读入颗粒数量的参数
sscanf(buf + next_eql,"%d",&p->chip_num);
//若存在相关的参数,则将buf指向=后面的位置,并将buf位置的数据写入到p文件中,下同
}else if((res_eql=strcmp(buf,"dram capacity")) ==0){//读入dram的容量
sscanf(buf + next_eql,"%d",&p->dram_capacity);
}else if((res_eql=strcmp(buf,"cpu sdram")) ==0){ //读入片内容量
sscanf(buf + next_eql,"%d",&p->cpu_sdram);
}else if((res_eql=strcmp(buf,"channel number")) ==0){//读入通道数量
sscanf(buf + next_eql,"%d",&p->channel_number);
}else if((res_eql=strcmp(buf,"die number")) ==0){ //读入晶圆数量
sscanf(buf + next_eql,"%d",&p->die_chip);
}else if((res_eql=strcmp(buf,"plane number")) ==0){ //读入分组数量
sscanf(buf + next_eql,"%d",&p->plane_die);
}else if((res_eql=strcmp(buf,"block number")) ==0){ //读入闪存块数量
sscanf(buf + next_eql,"%d",&p->block_plane);
}else if((res_eql=strcmp(buf,"page number")) ==0){ //读入闪存页数量
sscanf(buf + next_eql,"%d",&p->page_block);
}else if((res_eql=strcmp(buf,"subpage page")) ==0){ //读入子页数量
sscanf(buf + next_eql,"%d",&p->subpage_page);
}else if((res_eql=strcmp(buf,"page capacity")) ==0){ //读入闪存页大小
sscanf(buf + next_eql,"%d",&p->page_capacity);
}else if((res_eql=strcmp(buf,"subpage capacity")) ==0){ //读入子页大小
sscanf(buf + next_eql,"%d",&p->subpage_capacity);
}else if((res_eql=strcmp(buf,"t_PROG")) ==0){ //读入时序的相关参数,下同
sscanf(buf + next_eql,"%d",&p->time_characteristics.tPROG);
}else if((res_eql=strcmp(buf,"t_DBSY")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tDBSY);
}else if((res_eql=strcmp(buf,"t_BERS")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tBERS);
}else if((res_eql=strcmp(buf,"t_CLS")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCLS);
}else if((res_eql=strcmp(buf,"t_CLH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCLH);
}else if((res_eql=strcmp(buf,"t_CS")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCS);
}else if((res_eql=strcmp(buf,"t_CH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCH);
}else if((res_eql=strcmp(buf,"t_WP")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tWP);
}else if((res_eql=strcmp(buf,"t_ALS")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tALS);
}else if((res_eql=strcmp(buf,"t_ALH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tALH);
}else if((res_eql=strcmp(buf,"t_DS")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tDS);
}else if((res_eql=strcmp(buf,"t_DH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tDH);
}else if((res_eql=strcmp(buf,"t_WC")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tWC);
}else if((res_eql=strcmp(buf,"t_WH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tWH);
}else if((res_eql=strcmp(buf,"t_ADL")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tADL);
}else if((res_eql=strcmp(buf,"t_R")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tR);
}else if((res_eql=strcmp(buf,"t_AR")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tAR);
}else if((res_eql=strcmp(buf,"t_CLR")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCLR);
}else if((res_eql=strcmp(buf,"t_RR")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRR);
}else if((res_eql=strcmp(buf,"t_RP")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRP);
}else if((res_eql=strcmp(buf,"t_WB")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tWB);
}else if((res_eql=strcmp(buf,"t_RC")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRC);
}else if((res_eql=strcmp(buf,"t_REA")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tREA);
}else if((res_eql=strcmp(buf,"t_CEA")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCEA);
}else if((res_eql=strcmp(buf,"t_RHZ")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRHZ);
}else if((res_eql=strcmp(buf,"t_CHZ")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCHZ);
}else if((res_eql=strcmp(buf,"t_RHOH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRHOH);
}else if((res_eql=strcmp(buf,"t_RLOH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRLOH);
}else if((res_eql=strcmp(buf,"t_COH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tCOH);
}else if((res_eql=strcmp(buf,"t_REH")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tREH);
}else if((res_eql=strcmp(buf,"t_IR")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tIR);
}else if((res_eql=strcmp(buf,"t_RHW")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRHW);
}else if((res_eql=strcmp(buf,"t_WHR")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tWHR);
}else if((res_eql=strcmp(buf,"t_RST")) ==0){
sscanf(buf + next_eql,"%d",&p->time_characteristics.tRST);
}else if((res_eql=strcmp(buf,"erase limit")) ==0){ //读入擦除上限
sscanf(buf + next_eql,"%d",&p->ers_limit);
}else if((res_eql=strcmp(buf,"flash operating current")) ==0){//读入工作电流参数
sscanf(buf + next_eql,"%lf",&p->operating_current);
}else if((res_eql=strcmp(buf,"flash supply voltage")) ==0){
sscanf(buf + next_eql,"%lf",&p->supply_voltage);
}else if((res_eql=strcmp(buf,"dram active current")) ==0){
sscanf(buf + next_eql,"%lf",&p->dram_active_current); //读入dram的状态
}else if((res_eql=strcmp(buf,"dram standby current")) ==0){
sscanf(buf + next_eql,"%lf",&p->dram_standby_current);
}else if((res_eql=strcmp(buf,"dram refresh current")) ==0){
sscanf(buf + next_eql,"%lf",&p->dram_refresh_current);
}else if((res_eql=strcmp(buf,"dram voltage")) ==0){
sscanf(buf + next_eql,"%lf",&p->dram_voltage);
}else if((res_eql=strcmp(buf,"address mapping")) ==0){ //读入映射的类型(页映射,块映射,fast映射)
sscanf(buf + next_eql,"%d",&p->address_mapping);
}else if((res_eql=strcmp(buf,"wear leveling")) ==0){ //磨损均衡算法
sscanf(buf + next_eql,"%d",&p->wear_leveling);
}else if((res_eql=strcmp(buf,"gc")) ==0){ //读入垃圾回收机制
sscanf(buf + next_eql,"%d",&p->gc);
}else if((res_eql=strcmp(buf,"clean in background")) ==0){ //读入清除操作是否能在前台完成 //
sscanf(buf + next_eql,"%d",&p->clean_in_background);
}else if((res_eql=strcmp(buf,"overprovide")) ==0){ //
sscanf(buf + next_eql,"%f",&p->overprovide);
}else if((res_eql=strcmp(buf,"gc threshold")) ==0){ //读入gc阀值
sscanf(buf + next_eql,"%f",&p->gc_threshold);
}else if((res_eql=strcmp(buf,"buffer management")) ==0){ //读入是否启动缓存管理
sscanf(buf + next_eql,"%d",&p->buffer_management);
}else if((res_eql=strcmp(buf,"scheduling algorithm")) ==0){ //读入选用何种调度算法
sscanf(buf + next_eql,"%d",&p->scheduling_algorithm);
}else if((res_eql=strcmp(buf,"quick table radio")) ==0){
sscanf(buf + next_eql,"%f",&p->quick_radio);
}else if((res_eql=strcmp(buf,"related mapping")) ==0){
sscanf(buf + next_eql,"%d",&p->related_mapping);
}else if((res_eql=strcmp(buf,"striping")) ==0){ //读入是否启用striping方式
sscanf(buf + next_eql,"%d",&p->striping);
}else if((res_eql=strcmp(buf,"interleaving")) ==0){ //读入是否启用交错命令
sscanf(buf + next_eql,"%d",&p->interleaving);
}else if((res_eql=strcmp(buf,"pipelining")) ==0){
sscanf(buf + next_eql,"%d",&p->pipelining);
}else if((res_eql=strcmp(buf,"time_step")) ==0){ //读入时间步长
sscanf(buf + next_eql,"%d",&p->time_step);
}else if((res_eql=strcmp(buf,"small large write")) ==0){ //读入大量写的阀值
sscanf(buf + next_eql,"%d",&p->small_large_write);
}else if((res_eql=strcmp(buf,"active write threshold")) ==0){
sscanf(buf + next_eql,"%d",&p->threshold_fixed_adjust);
}else if((res_eql=strcmp(buf,"threshold value")) ==0){
sscanf(buf + next_eql,"%f",&p->threshold_value);
}else if((res_eql=strcmp(buf,"active write")) ==0){ //读入是否执行主动写
sscanf(buf + next_eql,"%d",&p->active_write);
}else if((res_eql=strcmp(buf,"gc hard threshold")) ==0){ //读入启用GC操作不可中断的阀值
sscanf(buf + next_eql,"%f",&p->gc_hard_threshold);
}else if((res_eql=strcmp(buf,"allocation")) ==0){ //读入分配方式
sscanf(buf + next_eql,"%d",&p->allocation_scheme);
}else if((res_eql=strcmp(buf,"static_allocation")) ==0){ //读入静态分配的方式
sscanf(buf + next_eql,"%d",&p->static_allocation);
}else if((res_eql=strcmp(buf,"dynamic_allocation")) ==0){ //读入动态分配的方式
sscanf(buf + next_eql,"%d",&p->dynamic_allocation);
}else if((res_eql=strcmp(buf,"advanced command")) ==0){
sscanf(buf + next_eql,"%d",&p->advanced_commands);
}else if((res_eql=strcmp(buf,"advanced command priority")) ==0){//读入分组操作和交错操作的优先级
sscanf(buf + next_eql,"%d",&p->ad_priority);
}else if((res_eql=strcmp(buf,"advanced command priority2")) ==0){//读入通道优先级
sscanf(buf + next_eql,"%d",&p->ad_priority2);
}else if((res_eql=strcmp(buf,"greed CB command")) ==0){ //读入是否使用数据迁移命令
sscanf(buf + next_eql,"%d",&p->greed_CB_ad);
}else if((res_eql=strcmp(buf,"greed MPW command")) ==0){ //读入是否使用多分组写操作
sscanf(buf + next_eql,"%d",&p->greed_MPW_ad);
}else if((res_eql=strcmp(buf,"aged")) ==0){ //读入是否需要旧化
sscanf(buf + next_eql,"%d",&p->aged);
}else if((res_eql=strcmp(buf,"aged ratio")) ==0){ //读入旧化率
sscanf(buf + next_eql,"%f",&p->aged_ratio);
}else if((res_eql=strcmp(buf,"queue_length")) ==0){ //读入请求队列长度
sscanf(buf + next_eql,"%d",&p->queue_length);
}else if((res_eql=strncmp(buf,"chip number",11)) ==0) //这里不是应该把channel_number的值读给i的吗???
{
sscanf(buf+12,"%d",&i);//将"="后的值读给i,i代表的是chip颗粒数
//疑问:这里怎么是将buf+12也就是 = 后面代表颗粒值的参数赋值给了i?然后i怎么就用在了chip_channel[]
//这个数组中呢??不科学啊。。。。
sscanf(buf + next_eql,"%d",&p->chip_channel[i]); //而且这里怎么就将 = 后面的值也就是i又写到了这个chip_channel数组中呢?
}else{ //这里到底是怎么回事啊。。。。。
printf("don't match\t %s\n",buf);
}
memset(buf,0,BUFSIZE); //重新将buf区清零
}
/**************************************************************************************************************
fclose是一个函数名,功能是关闭一个流。注意:使用fclose()函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区。
函数原型:int fclose( FILE *fp );
返回值:如果流成功关闭,fclose 返回 0,否则返回EOF(-1)。(如果流为NULL,而且程序可以继续执行,fclose设定error number给EINVAL,并返回EOF。)
**************************************************************************************************************/
fclose(fp); //关闭文件流
fclose(fp1);
fclose(fp2);
return p;
}