UBIFS文件系统分析五之重要数据结构

      用leeming的话来说,一个大的工程中,最最核心的往往是数据结构体的定义。所以看代码不急着看c文件,而是主要看document和h文件,来理解设计者的思路,这样才能走对路。

1. struct ubi_device

       UBI中对于一个UBI设备的抽象是以struct ubi_device来定义,其中包括了该UBI设备的各种信息。

struct ubi_device {

       structcdev cdev;

       structdevice dev;

       intubi_num;//UBI设备的标号,在ubiattach用户程序时以-d选项来输入

       charubi_name[sizeof(UBI_NAME_STR)+5];//ubi设备的名称

       intvol_count;//在该UBI设备中有多少个volume

       structubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];

       spinlock_tvolumes_lock;

       intref_count;

       intimage_seq;

       intrsvd_pebs;//保留的LEB数目

       intavail_pebs;//可用的LEB数目

       intbeb_rsvd_pebs;//为坏块处理而保留的LEB数目

       intbeb_rsvd_level;//为坏块处理而保留的LEB的正常数目

       intautoresize_vol_id;

       intvtbl_slots;

       intvtbl_size;//volume表的大小(bytes)

       structubi_vtbl_record *vtbl;//内存中volume表的拷贝

       structmutex device_mutex;

       intmax_ec;//最大的erasecounter

       /*Note, mean_ec is not updated run-time - should be fixed */

       intmean_ec;//平均erasecounter

       /*EBA sub-system's stuff */

       unsignedlong long global_sqnum;

       spinlock_tltree_lock;

       structrb_root ltree;

       structmutex alc_mutex;

       /*Wear-leveling sub-system's stuff */

       structrb_root used;//一个红黑树,其中是已用的blcok

       structrb_root erroneous;// RB-tree of erroneous used physical eraseblocks

       structrb_root free;//红黑树的根,其中是没有用到的block

       structrb_root scrub;//需要擦除的blcok

       structlist_head pq[UBI_PROT_QUEUE_LEN];

       intpq_head;

       spinlock_twl_lock;

       structmutex move_mutex;

       structrw_semaphore work_sem;

       intwl_scheduled;

       structubi_wl_entry **lookuptbl;// a table to quickly find a &struct ubi_wl_entryobject for any  physical eraseblock,,一个struct ubi_wl_entry类型的数组,以pnum为下标,记录该UBI设备的每一个block

       structubi_wl_entry *move_from;// physical eraseblock from where the data is beingmoved

       structubi_wl_entry *move_to;// physical eraseblock where the data is being moved to

       intmove_to_put;//标志位,用于标志目的LEB是否被put

       structlist_head works;// list of pending works

       intworks_count;// count of pending works

       structtask_struct *bgt_thread;//UBI的后台进程

       intthread_enabled;

       charbgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];//后台进程的名字

       structnotifier_block reboot_notifier;//内核通知链

       /*I/O sub-system's stuff */

       longlong flash_size;//MTD分区的大小

       intpeb_count;//LEB的数目

       intpeb_size;//LEB的大小(每一个block的大小)

       intbad_peb_count;//坏块数目

       intgood_peb_count;//能使用的LEB数目

       interroneous_peb_count;

       intmax_erroneous;

       intmin_io_size;//最小操作单元的大小,也就是一个page的大小

       inthdrs_min_io_size;

       intro_mode;

       intleb_size;//逻辑块的大小,一般等于peb_size

       intleb_start;//逻辑块块从物理块中那一块开始算,也就是之前的物理块保留用于其他目的

       intec_hdr_alsize;// size of the EC header aligned to @hdrs_min_io_size

       intvid_hdr_alsize; //size of the VID header aligned to @hdrs_min_io_size

       intvid_hdr_offset;//VID头部在一块之中的偏移量。一般是一个pagesize

       intvid_hdr_aloffset;// starting offset of the VID header aligned to @hdrs_min_io_size

       intvid_hdr_shift// contains @vid_hdr_offset - @vid_hdr_aloffset

       unsignedint bad_allowed:1;

       unsignedint nor_flash:1;// non-zero if working on top of NOR flash

       structmtd_info *mtd;//指向MTD分区信息,我们知道,UBI层是构建在MTD层之上的。

       void*peb_buf1;//一个缓冲区,大小为一个block的大小

       void*peb_buf2; //一个缓冲区,大小为一个block的大小

       structmutex buf_mutex;

       structmutex ckvol_mutex;

#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID

       void*dbg_peb_buf;

       structmutex dbg_buf_mutex;

#endif

};

2. struct ubi_vtbl_record

下一个重要的结构体structubi_vtbl_record,在认识这个结构体之前我们先看一副截图,

这幅截图是我们在attach一个设备时候的打印内容,红色的划线部分是我们要注意的内容:internalvolume

什么是internalvolume?它是与下面的uservolume相区别的。

internal volume是内核使用来保持相应的信息的,那么它保持的是什么呢?它保持的是volumetable。它是以structubi_vtbl_record数据结构的格式来保持的。

struct ubi_vtbl_record {

       __be32  reserved_pebs;// how many physicaleraseblocks are reserved for this volume

       __be32  alignment;// volume alignment

       __be32  data_pad;// how many bytes are unused at theend of the each physical eraseblock to satisfy the requested alignment

       __u8    vol_type;//volume的类型,分为动态和静态两种,动态volume可以动态的改变它的大小

       __u8    upd_marker;

       __be16  name_len;//volume name length

       __u8    name[UBI_VOL_NAME_MAX+1]; //volume name

       __u8    flags;

       __u8    padding[23];

       __be32  crc;

} __attribute__ ((packed));

3. struct ubi_volume

struct ubi_volume是对UBI设备上每一个volume的抽象。

struct ubi_volume {

       structdevice dev;

       structcdev cdev;

       structubi_device *ubi;//该volume在哪一个UBI设备上

       intvol_id;//volume标号

       intref_count;//引用次数(不知道什么用途)

       intreaders;// number of users holding this volume in read-only mode

       intwriters;// number of users holding this volume in read-write mode

       intexclusive;// whether somebody holds this volume in exclusive mode

       intreserved_pebs;//该volume中保留的peb数

       intvol_type;//volume类型

       intusable_leb_size;// logical eraseblock size without padding

       intused_ebs//可用PEB数目

       intlast_eb_bytes;// how many bytes are stored in the last logical eraseblock

       longlong used_bytes;//已用空间大小

       intalignment;

       intdata_pad;

       intname_len;//volume名字的长度

       charname[UBI_VOL_NAME_MAX + 1];

       intupd_ebs;

       intch_lnum; LEB number which is being changing by the atomic LEB change operation(这样在后面修改LEB数据的操作中可以看到)

       intch_dtype;

       longlong upd_bytes;

       longlong upd_received;

       void*upd_buf;

       int*eba_tbl;// EBA table of this volume,极其重要,LEB到PEB得影射关系需要查该表来获得

       unsignedint checked:1;

       unsignedint corrupted:1;

       unsignedint upd_marker:1;

       unsignedint updating:1;

       unsignedint changing_leb:1;

       unsignedint direct_writes:1;

};

4. struct ubi_scan_info

这个结构体是在attach的过程中使用的。在attach的过程中,UBIFS需要获知该设备上每一个PEB的状态,然后为重新挂载文件系统做准备。

struct ubi_scan_info {

       structrb_root volumes;//volume的红黑树的根节点

       //下面是4个链表,是在扫描的过程将扫描的block进行分类,然后连接到下面4个链表中的其中一个。

       structlist_head corr;

       structlist_head free;

       structlist_head erase;

       structlist_head alien;

       intbad_peb_count;//坏块数

       intvols_found;//volume数

       inthighest_vol_id;//volume 的最高标号

       intalien_peb_count;

       intis_empty;//标志位,用于表示该UBI设备是否为空的,在上面所说的扫描过程被置位

       intmin_ec;//最小erasecounter

       intmax_ec;//最大erasecounter

       unsignedlong long max_sqnum;//64位的sqnum

       intmean_ec;//平均erasecounter

       uint64_tec_sum;

       intec_count;

       intcorr_count;

};

5 struct ubi_scan_leb

在上面的structubi_scan_info中我们说到了在attach操作中的扫描过程,并且说到了struct ubi_scan_info中的4个队列,是将扫描的每一个block的信息抽象,然后挂载到这些队列中去,下面就简单的说一下对于block扫描信息的抽象。

struct ubi_scan_leb {

       intec;//erase counter,用于均衡损耗目的,以后详细介绍

       //每一个卷的eba_table就是由下面两个成员构成的。

       intpnum;//物理块标号

       intlnum;//逻辑块标号

       intscrub;

       unsignedlong long sqnum;

       union{

              structrb_node rb;

              structlist_head list;

       }u;

};

6. struct ubi_ec_hdr

我们知道UBIFS是一个Wear-level的文件系统,即均衡损耗。我们就以struct ubi_ec_hdr这个开始重要结构体的介绍。

struct ubi_ec_hdr {

       __be32  magic;

       __u8    version;

       __u8    padding1[3];

       __be64  ec; /* Warning: the current limit is 31-bitanyway! */

       __be32  vid_hdr_offset;

       __be32  data_offset;

       __be32  image_seq;

       __u8    padding2[32];

       __be32  hdr_crc;

} __attribute__ ((packed));

我们注意其中的一个成员变量为_be64  ec,ec是什么,ec就是erase counter。我们知道NANDFLASH是的擦除是有次数限制的,当擦除的次数太多的时候,就会变成坏块。什么是均衡损耗,就是在文件系统的管理下,我们不能对其中的一块进行过多的擦除操作。

我们来看函数ensure_wear_leveling,它只要是来判断UBI设备是否需要进行均衡损耗的相关处理,这儿就有两个问题了。1.它判断的依据是什么。2.它会进行什么样的相关来避免对一个可擦出块进行过多的擦除操作。

那么我们先来回答第一个问题,在WL子系统中,所有的可擦出块都归WL子系统来进行管理。这是一个RB数,我们来其中的每一个结点。

struct ubi_wl_entry {

       union{

              structrb_node rb;

              structlist_head list;

       }u;

       intec;

       intpnum;

};

说白了,WL只关心一个东西,那么就是ec的数值。下面是wear_leveling_worker函数中的一段核心代码:

              e1= rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, u.rb);

              e2= find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);

if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD))

从used中队里中取出一个LEB,显然EC是最小的(每擦除一次,EC值加一),再从free队列中取出一个EC值最大的LEB。

如果两个LEB的ec差值大于了UBI_WL_THRESHOLD,那么就需要进行WL操作了。

那么多操作是什么呢?

err = ubi_eba_copy_leb(ubi, e1->pnum,e2->pnum, vid_hdr);

将内容从一个LEB搬到另外一个LEB中去。

6.struct ubi_vid_hdr

在上面EC头部中有一个成员变量是vid_hdr_offset,是指vid_hdr在FLASH中的偏移量,接着分析第二重要的数据结构struct ubi_vid_hdr。

struct ubi_vid_hdr {

       __be32  magic;

       __u8    version;

       __u8    vol_type;

       __u8    copy_flag;

       __u8    compat;

       __be32  vol_id;

       __be32  lnum;

       __u8    padding1[4];

       __be32  data_size;

       __be32  used_ebs;

       __be32  data_pad;

       __be32  data_crc;

       __u8    padding2[4];

       __be64  sqnum;

       __u8    padding3[12];

       __be32  hdr_crc;

} __attribute__ ((packed));

这其中最重要的成员变量是__be32  vol_id和__be32  lnum。vol_id是标示该LEB属于哪儿一个volume。Lnum是指与该PNUM相对于的lnum。对于上层而言,我们操作的是逻辑块,也就是lnum,但是最终需要将数据写进pnum中去,在ubi_eba_write_leb函数中有这样的一句:

pnum = vol->eba_tbl[lnum];

每一个volume都有一个eba_tbl,是在扫描的时候建立的。如果该lnum没有影射,那么调用ubi_wl_get_peb来获得一个pnum,并相应的修改volume的eba_tbl;

7. struct ubi_scan_volume

struct ubi_scan_volume {

       intvol_id;//volume标号

       inthighest_lnum;//该volume中的最高逻辑块标号

       intleb_count;//leb数目

       intvol_type;//volume类型

       intused_ebs;//已用PEB数

       intlast_data_size;

       intdata_pad;

       intcompat;

       structrb_node rb;//不清楚具体目的,猜想应该是一个节点的cache,缓存的是刚刚访问的结点

       structrb_root root;

};

这儿主要注意一下上面的structrb_root root变量,这个成员是一个红黑树的根,用来链接在扫描的过程中发现的属于该volume的PEB。

这个结构体是在扫描的过程中读VID头部建立起来的关于volume的临时信息。

 

 

在ubifs-media.h中定义了很多的结构体,下面简单的解释一下。

在《A brief 。。。》中讲到了,UBIFS采用的node-structure。它的所有的数据都是以node的形式处理的。

struct ubifs_ch,其中ch的意思是指common header,是下面的所有结构体的共同部分。

struct ubifs_ino_node,是用来保存inode结点相关信息的。

struct ubifs_dent_node,用来来保存dent的相关信息。关于dent和inode请参考VFS部分的相关内容。

struct ubifs_data_node,用来保存具体数据的结点。

struct ubifs_trun_node,用来在trunk的时候写入journal区中的,只存在于journal区中。

struct ubifs_pad_node,用来进行数据填充的。

struct ubifs_sb_node,超结块结点, 用来记载superblock的相关信息,只存在在superblock 区中。

struct ubifs_mst_node,记录master结点的数据结构,记载node树的根结点以及其他信息。只存在在master area中。

struct ubifs_ref_node ,用于在更新数据的时候写入journal区,在commit的时候更新index树和LPT树,只存在在journal区中。

struct ubifs_idx_node,idx结点的头部,关于idx结点,请参考《a briefintroduction to design of UBIFS》,只存在在main区中。

struct ubifs_branch,idx结点中的分支。

struct ubifs_cs_node,cs = commit start,用于在journal中表示一次commit的开始,只存在在journal区中。一次commit由一个ubifs_cs_node 和若干ubifs_ref_node 组成。

struct ubifs_orph_node ,用于在orphan区中记录相关信息的结点,关于orphan请参考《a brief introduction to designof UBIFS》。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值