yaffs2源码学习2:chunk和block

  • 一、Nand Flash介绍
  • 二、Chunk相关操作
    • 2.1 chunk记录数据的种类
    • 2.2 chunk的使用情况
    • 2.3 写入chunk
    • 2.4 删除chunk
  • 三、Block相关操作
    • 3.1 block的内容
    • 3.2 block的删除

一、Nand Flash介绍

非易失性闪速存储器Flash具有速度快、成本低、密度大的特点,被广泛应用于嵌入式系统中。Flash存储器主要有NOR和NAND两种类型。NOR型比较适合存储程序代码;NAND型则可用作大容量数据存储。
一块Nand Flash芯片被分为了很多block,而每一个block又由很多个chunk构成,每个chunk由data和spare这两个区域组成,data区域存放文件数据,spare区域存放坏块信息,ECC校验等。我们以K9F2G08X0A为例进行介绍,如图:

  1. K9F2G08X0A是2,112Mbit(2,214,592,512 bit)大小,共包含2048个BLOCK,每个BLOCK包含64个页,每个页包含2112个字节。
  2. 一个大小为2112的页由数据区域(2K)和SPARE区域(64Bytes)组成,其中SPARE区域用来存储坏块信息、ECC校验等。
  3. 读数据时可以按字节读取,但擦写单位最小是一个块。
  4. 器件中的坏块是随机分布的。出厂时坏块被标记在坏块的第一页或第二页,它们被标记为非0xFF的值。第一次使用时应扫描FLASH并记录坏块信息。
  5. 所有flash器件都受位交换现象的困扰。在使用中应采用错误探测/错误更正(EDC/ECC)算法确保数据可靠性。一旦发现ECC错误,应将数据所在块标记为坏块

对于数据,最基本的存储和删除单元就是chunk和block,所以这一部分我们介绍yaffs2中对于chunk和block相关操作的源码。

二、Chunk相关操作

2.1 chunk记录数据的种类

Nand Flash上共记录着三类数据,check point data、object header与file data。记录chunk状态的数据结构如下:

struct yaffs_ext_tags {unsigned chunk_used;  /*  Status of the chunk: used or unused */unsigned obj_id;  /* If 0 this is not used */unsigned chunk_id;   /* If 0 this is a header, else a data chunk */unsigned n_bytes; /* Only valid for data chunks *//* The following stuff only has meaning when we read */enum yaffs_ecc_result ecc_result;unsigned block_bad;/* YAFFS 1 stuff */unsigned is_deleted;  /* The chunk is marked deleted */unsigned serial_number;    /* Yaffs1 2-bit serial number *//* YAFFS2 stuff */unsigned seq_number;  /* The sequence number of this block *//* Extra info if this is an object header (YAFFS2 only) */unsigned extra_available;  /* Extra info available if not zero */unsigned extra_parent_id; /* The parent object */unsigned extra_is_shrink;    /* Is it a shrink header? */unsigned extra_shadows; /* Does this shadow another object? */enum yaffs_obj_type extra_obj_type;   /* What object type? */loff_t extra_file_size;      /* Length if it is a file */unsigned extra_equiv_id;    /* Equivalent object for a hard link */
};
  • check point data YAFFS2按照block来分配存储空间记录check point data,即一个block要么全用于记录check point data,要么全用于记录普通数据。而chunk的tag.seq_number被复用于“记录block seq_number”,或“表示block记录着check point data”。这个复用的可能性是建立在block. seq_number的大小是在一个固定的区间内。
  • object header tag.chunkId等于0,表示相应chunk的data area记录着object header,并可由tag.objectId找到相应的文件。
  • file data tag.chunkId不等于0,则相应chunk的data area记录着file data,并且tag.chunkId表示该chunk在文件内的logical chunk id。此外可由tag.objectId找到相应的文件。

2.2 chunk的使用情况

在dev结构中,有个参数u8 *chunk_bits,记录着chunk的使用情况,每一个bit对应一个chunk,YAFFS2用该数据记录着chunk是否正在被使用。这信息在运行时只存在于内存中,当YAFFS2被unmout时,该数组当作check point data被记录下来,在下一次的mount时被读出并被恢复。关于某一chunk对应的bit的更改函数在文件“yaffs_bitmap.c”中,代码如下,内容比较简单,就不在详细的说明了,根据函数名也大概知道该函数做了什么工作。

static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk)
{if (blk < (int)dev->internal_start_block ||blk > (int)dev->internal_end_block) {yaffs_trace(YAFFS_TRACE_ERROR,"BlockBits block %d is not valid",blk);BUG();}return dev->chunk_bits +(dev->chunk_bit_stride * (blk - dev->internal_start_block));
}void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk)
{if (blk < (int)dev->internal_start_block ||blk > (int)dev->internal_end_block ||chunk < 0 || chunk >= (int)dev->param.chunks_per_block) {yaffs_trace(YAFFS_TRACE_ERROR,"Chunk Id (%d:%d) invalid",blk, chunk);BUG();}
}void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);memset(blk_bits, 0, dev->chunk_bit_stride);
}void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);yaffs_verify_chunk_bit_id(dev, blk, chunk);blk_bits[chunk / 8] &= ~(1 << (chunk & 7));
}void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);yaffs_verify_chunk_bit_id(dev, blk, chunk);blk_bits[chunk / 8] |= (1 << (chunk & 7));
}int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);yaffs_verify_chunk_bit_id(dev, blk, chunk);return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
}int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);int i;for (i = 0; i < dev->chunk_bit_stride; i++) {if (*blk_bits)return 1;blk_bits++;}return 0;
}int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk)
{u8 *blk_bits = yaffs_block_bits(dev, blk);int i;int n = 0;for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++)n += hweight8(*blk_bits);return n;
}

2.3 写入chunk

向chunk中写入数据就在yaffs2的核心代码文件“yaffs_guts.c”文件中,这里面比较重要的关于写数据的函数是“yaffs_alloc_chunk”和“yaffs_write_new_chunk”,代表着分配一个新的chunk以及在新的chunk中写入数据。代码如下,我们在其中删去打印信息的代码。

static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,struct yaffs_block_info **block_ptr)
{int ret_val;struct yaffs_block_info *bi;if (dev->alloc_block < 0) {/* Get next block to allocate off */dev->alloc_block = yaffs_find_alloc_block(dev);dev->alloc_page = 0;}/*use_reserver表示是否使用保留空间,一般情况下都是0,只有在垃圾收集需要分配存储空间时该参数置为1*/if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) {return -1;}if (dev->alloc_block >= 0) {bi = yaffs_get_block_info(dev, dev->alloc_block);ret_val = (dev->alloc_block * dev->param.chunks_per_block) +dev->alloc_page;        /*ret_val是该chunk在整个device内的总序号*/bi->pages_in_use++;yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page);  /*更改相应位图的页*/dev->alloc_page++;dev->n_free_chunks--;/* 如果block已经写满了,设置相应块状态*/if (dev->alloc_page >= dev->param.chunks_per_block) {bi->block_state = YAFFS_BLOCK_STATE_FULL;dev->alloc_block = -1;}if (block_ptr)*block_ptr = bi;return ret_val;}return -1;
}static int yaffs_write_new_chunk(struct yaffs_dev *dev,const u8 *data,struct yaffs_ext_tags *tags, int use_reserver)
{u32 attempts = 0;int write_ok = 0;int chunk;yaffs2_checkpt_invalidate(dev);do {struct yaffs_block_info *bi = 0;int erased_ok = 0;chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);if (chunk < 0) {/* no space */break;}/* let's give it a try */attempts++;if (dev->param.always_check_erased)bi->skip_erased_check = 0;if (!bi->skip_erased_check) {erased_ok = yaffs_check_chunk_erased(dev, chunk);if (erased_ok != YAFFS_OK) {yaffs_chunk_del(dev, chunk, 1, __LINE__);yaffs_skip_rest_of_block(dev);continue;}}write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);if (!bi->skip_erased_check)write_ok = yaffs_verify_chunk_written(dev, chunk, data, tags);if (write_ok != YAFFS_OK) {yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);continue;}bi->skip_erased_check = 1;yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);} while (write_ok != YAFFS_OK &&(yaffs_wr_attempts == 0 || attempts <= yaffs_wr_attempts));if (!write_ok)chunk = -1;if (attempts > 1) {dev->n_retried_writes += (attempts - 1);}return chunk;
}

最终的写入是使用函数“yaffs_wr_chunk_tags_nand”,其在写入数据时使用了语句dev->tagger.write_chunk_tags_fn,采用了指针函数的方法,使得函数使用更加灵活,对于函数的初始化放在其初始化在文件“yaffs_tagscompat.c”文件中,函数“yaffs_tags_compat_wr”是其最终使用的写入数据的函数。如下表:

static int yaffs_tags_compat_wr(struct yaffs_dev *dev, int nand_chunk,const u8 *data, const struct yaffs_ext_tags *ext_tags)
{struct yaffs_spare spare;struct yaffs_tags tags;yaffs_spare_init(&spare);if (ext_tags->is_deleted)spare.page_status = 0;else {tags.obj_id = ext_tags->obj_id;tags.chunk_id = ext_tags->chunk_id;tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);if (dev->data_bytes_per_chunk >= 1024)tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;elsetags.n_bytes_msb = 3;tags.serial_number = ext_tags->serial_number;if (!dev->param.use_nand_ecc && data) {yaffs_ecc_calc(data, spare.ecc1);yaffs_ecc_calc(&data[256], spare.ecc2);}yaffs_load_tags_to_spare(dev, &spare, &tags);}return yaffs_wr_nand(dev, nand_chunk, data, &spare);
}

这里面就包含了ecc校验码的生成,spare数据的计算,最终的写入调入了函数yaffs_wr_nand,其最终的写入数据就是调用的dev->drv.drv_write_chunk_fn,同样是指针函数,其最终指向的是底层flash驱动函数。至此完成了chunk的写入。

2.4 删除chunk

因为Nand Flash以chunk写入,以block删除的特点,这里的删除chunk就是指将这个chunk表示为无效,等到block删除时在一块删除。“yaffs_chunk_del”和“yaffs_soft_del_chunk”均可以删除一个chunk,前者更新了u8 *chunk_bits和bi->pagesInUse,而后者只是更新了soft_del_pages。另外,前者会引起erase block操作(调用函数yaffs_block_became_dirty),而后者不会。具体代码如下:

void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash,int lyn)
{int block;int page;struct yaffs_ext_tags tags;struct yaffs_block_info *bi;if (chunk_id <= 0)return;dev->n_deletions++;block = chunk_id / dev->param.chunks_per_block;page = chunk_id % dev->param.chunks_per_block;bi = yaffs_get_block_info(dev, block);yaffs2_update_oldest_dirty_seq(dev, block, bi);if (!dev->param.is_yaffs2 && mark_flash &&bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) {memset(&tags, 0, sizeof(tags));tags.is_deleted = 1;yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags);yaffs_handle_chunk_update(dev, chunk_id, &tags);} else {dev->n_unmarked_deletions++;}if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||bi->block_state == YAFFS_BLOCK_STATE_FULL ||bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {dev->n_free_chunks++;yaffs_clear_chunk_bit(dev, block, page);bi->pages_in_use--;if (bi->pages_in_use == 0 &&!bi->has_shrink_hdr &&bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING &&bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) {yaffs_block_became_dirty(dev, block);}}
}static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)
{struct yaffs_block_info *the_block;unsigned block_no;block_no = chunk / dev->param.chunks_per_block;the_block = yaffs_get_block_info(dev, block_no);if (the_block) {the_block->soft_del_pages++;dev->n_free_chunks++;yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);}
}

三、Block相关操作

3.1 block的内容

结构体yaffs_BlockInfo记录着block的运行时信息,在yaffs2文件系统运行时,每一个块的BlockInfo都被一个数组记录着,dev->block_info可以指向这个数组,该信息在运行时存在于内存中,当Yaffs2被卸载时,该数组当做检查点数据被记录下来,下一次挂载文件系统时读出并被恢复。yaffs_BlockInfo结构体如下:

struct yaffs_block_info {s32 soft_del_pages:10;  /* 软删除的页的数量*/s32 pages_in_use:10;   /* 已分配使用的page的数量*/u32 block_state:4;        /* 块的状态 */u32 needs_retiring:1; /* 块上数据失效,是否需要回收*/u32 skip_erased_check:1;/* 是否跳过回收检查*/u32 gc_prioritise:1;  /* ecc校验失效,块需要优先GC*/u32 chunk_error_strikes:3;   /* 允许ECC校验失效的次数*/u32 has_summary:1;     /*块有summary */u32 has_shrink_hdr:1;   /* 一个块至少有一个 shrink header */u32 seq_number;           /*各block被分配出去的先后顺序,垃圾回收时判断该block是否适合回收*/
};

各个参数的含义已经在注释中解释过了,不在过多的叙述,seq_number表示block被使用的前后顺序,越小则表示被使用的越早,反之亦然。block的seq_number在gc与上电scan时起到了非常重要的作用。
另外,需要注意的是,文件系统运行时,全局信息结构体中dev->alloc_block记录着正在分配chunk的block索引,dev->alloc_page记录正在分配的block上已经分配出去的chunk数。正在分配的block的状态为“allocating”,当block上的chunk全部被分配,即全部被program过时,block的状态为“full”。

3.2 block的删除

对于block的重要操作就是“yaffs_guts.c”文件中的“yaffs_block_became_dirty”和“yaffs_nand.c”文件中的“yaffs_erase_block”和“yaffs_mark_bad”,其最主要就是在文件系统进行block回收时使用,这里不在进行详细的介绍,后续在分析到垃圾回收这个部分的时候,再着重进行代码分析。

yaffs2源码学习2:chunk和block相关推荐

  1. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  2. (0045) iOS 开发之MBProgressHUD 源码学习

    (0045) iOS 开发之MBProgressHUD 源码学习 第一部分:学习所得和分析线程 1.  学习到了kvo 的使用 和屏幕方向的旋转判断. 2. 如果调起这个 HUD 的方法不是在主线程调 ...

  3. 以太坊源码学习 -- EVM

    以太坊源码学习 – EVM 学习文档链接:here 一.虚拟机外 主要功能: 执行前将Transaction类型转化成Message,创建虚拟机(EVM)对象,计算一些Gas消耗,以及执行交易完毕后创 ...

  4. ASP.NET Core 源码学习之Logging[1]:Introduction

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  5. Java多线程之JUC包:Semaphore源码学习笔记

    若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...

  6. AFNetworking源码学习

    AFNetworking源码学习 简介 AFNetWorking是使用Objective-c开发iOS程序主流的网络请求开源库. AFNetworking组织结构 AFNetWorking主要分为5个 ...

  7. 以太坊源码学习(一) 正本清源

    以太坊源码学习(一)正本清源 背景 geth源码一直在不断增加,优化,发展到现在已经非常庞大,第一次看geth源码,会有不小的难度.虽然如此,还是可以从geth仓库的第一个commit开始,这时的代码 ...

  8. 博通Broadcom SDK源码学习与开发2——Bootloader功能和编译过程

    声明:原创作品,严禁用于商业目的. 本系列文章将全面剖析以Bcm33xxx芯片开发Cablemodem产品的SDK源码为例,从编译系统到各个功能模块进行分析与探讨. 文章目录 0.写在前篇 0.写在前 ...

  9. 博通Broadcom SDK源码学习与开发5——ECOS系统层剖析

    声明:原创作品,严禁用于商业目的. 本系列文章将全面剖析以Bcm33xxx芯片开发Cablemodem产品的SDK源码为例,从编译系统到各个功能模块进行分析与探讨. 文章目录 0.写在前篇 1. Op ...

  10. 源码学习-net/http

    package net/http是Go语言的主要应用场景之一web应用的基础,从中可以学习到大量前文提到的io,以及没有提到的sync包等一系列基础包的知识,代码量也相对较多,是一个源码学习的宝库.本 ...

最新文章

  1. 基于模糊聚类的色彩迁移算法
  2. php上传图文教程,PHP 上传图片、文件的方法
  3. linux系统上搭建vsftp服务
  4. Et.parse(xml) #39gbk#39 codec cant decode byte
  5. Boost::context模块callcc的throw测试程序
  6. CMOS密码安全攻略
  7. 大整数减法(信息学奥赛一本通-T1169)
  8. C#LeetCode刷题-数学
  9. java元注解_Java的元注解
  10. JavaScript之DOM对象(Event事件)
  11. 插件显示缩进_硬核,这 3 款 IDE 插件让你的代码牢不可破
  12. 转行学编程,女孩子适合web前端还是Java?
  13. QT+OPENCV+FFTW内存问题
  14. python预测模型类型,多变量时间序列的预测和建模指南(附Python代码)
  15. win10保护色设置及还原
  16. java职业规划怎么写_java个人职业生涯发展规划书范文
  17. elk logstach收集交换机日志
  18. 微信演示制作软件易企秀的上手体验(图文)
  19. 微生物的质谱鉴定原理
  20. 新浪微博开放平台开发-android客户端(1)

热门文章

  1. 营业执照验证php,基于PHP的营业执照识别示例代码-六派数据
  2. linux nginx配置81端口用于访问web81
  3. Python数据分析案例篇(一)泰坦尼克号数据分析
  4. 文件快速定位神器(C++小项目实战)
  5. 使用canvas将多张图片合并为一张
  6. 笔记本加装固态硬盘,安装Ubuntu
  7. OSS对象存储之阿里云和七牛云
  8. 学习java第14天
  9. 毛哥的快乐生活(1) 猫哥与毛哥
  10. 猫哥教你写爬虫 040--存储数据-作业