如何恢复 Linux 上删除的文件,第 6 部分_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
2
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3753 | 回复: 1   主题: 如何恢复 Linux 上删除的文件,第 6 部分        下一篇 
谁是天蝎
注册用户
等级:大元帅
经验:90210
发帖:106
精华:0
注册:2011-7-21
状态:离线
发送短消息息给谁是天蝎 加好友    发送短消息息给谁是天蝎 发消息
发表于: IP:您无权察看 2011-8-5 14:25:24 | [全部帖] [楼主帖] 楼主

reiserfs 是由 namesys 公司的 Hans Reiser 设计并开发的一种通用日志文件系统,它是第一个进入 Linux 标准内核日志文件系统。从诞生之日起,reiserfs 就由于其诸多非常有吸引力的特性而受到很多用户的青睐,迅速成为 Slackware 等发行版的默认文件系统。它也一度也是 SUSE Linux Enterprise 发行版上的默认文件系统,直到 2006 年 10 月 12 日 Novell 公司决定将默认文件系统转换到 ext3 为止。尽管其主要设计人员 Hans Reiser 由于涉嫌杀害妻子遭到指控而入狱,从而导致他不得不试图出售 namesys 公司来支付庞大的诉讼费用,但是 reiserfs 已经受到广大社区开发人员和用户的极大关注,有很多志愿者已经投入到新的 reiserfs 4 的开发工作中来。本文中的介绍都是基于最新的稳定版本 3.6 版本的,所引用的代码都基于 2.6.23 版本的内核。

reiserfs 最初的设计目标是为了改进 ext2 文件系统的性能,提高文件系统的利用率,并增强对包含大量文件的目录的处理能力(ext2/ext3 文件系统中一个目录下可以包含的子目录最多只能有 31998 个)。传统的 ext2 和 ufs 文件系统都采用了将文件数据和文件元数据分离开保存的形式,将元数据保存到索引节点中,将文件数据保存到单独的磁盘块中,并通过索引节点中的 i_block 数组利用直接索引和间接索引的形式在磁盘上定位文件数据。这种设计非常适合存储较大的文件(比如20KB以上),但是对于具有大量小文件的系统来说就存在一些问题。首先在于文件系统的利用率,由于 ext2 会将文件数据以数据块为单位(默认为 4KB)进行存储,因此对于存储只有几十个字节的文件来说,会造成空间的极大浪费。另外由于在读取文件时需要分别读取文件元数据和文件数据,加上多读取数据块的开销,ext2 文件系统在处理大量小文件时,性能会比较差。为了获取最好的性能和最大程度地利用磁盘空间,很多用户会在文件系统之上采用数据库之类的解决方案来存储这些小文件,因此会导致上层应用程序的接口极不统一。

为了解决上面提到的问题,reiserfs 为每个文件系统采用一棵经过专门优化的 B+ 树来组织所有的文件数据,并实现了很多新特性,例如元数据日志。为了提高文件系统的利用率,reiserfs 中采用了所谓的尾部封装(tail packing)设计,可以充分利用已分配磁盘块中的剩余空间来存储小文件。实际上,reiserfs 文件系统中存储的文件会比 ext2/ext3 大 5% - 6% 以上。下面让我们来探索一下 reiserfs 文件系统中数据在磁盘上究竟是如何存储的。

磁盘布局

与 ext2/ext3 类似,reiserfs 文件系统在创建时,也会将磁盘空间划分成固定大小的数据块。数据块从 0 开始编号,最多可以有 232 个数据块。因此如果采用默认的 4KB 大小的数据块,单个 reiserfs 文件系统的上限是 16TB。reiserfs 分区的前 64KB 保留给引导扇区、磁盘标签等使用。超级块(super block)从 64KB 开始,会占用一个数据块;之后是一个数据块位图,用来标识对应的数据块是否处于空闲状态。如果一个数据块位图可以标识 n 个数据块,那么 reiserfs 分区中的第 n 个数据块也都是这样一个数据块,用来标识此后(包括自己)n 的数据块的状态。reiserfs 文件系统的磁盘结构如图 1 所示。

图 1. reiserfs 分区磁盘布局
北京联动北方科技有限公司

与 ext2/ext3 类似,reiserfs 文件系统的一些关键信息也保存超级块中。reiserfs 的超级块使用一个 reiserfs_super_block 结构来表示,其定义如清单1 所示:

135 struct reiserfs_super_block_v1 {
      136 __le32 s_block_count; /* blocks count */
      137 __le32 s_free_blocks; /* free blocks count */
      138 __le32 s_root_block; /* root block number */
      139 struct journal_params s_journal;
      140 __le16 s_blocksize; /* block size */
      141 __le16 s_oid_maxsize; /* max size of object id array, see
      142 * get_objectid() commentary */
      143 __le16 s_oid_cursize; /* current size of object id array */
      144 __le16 s_umount_state; /* this is set to 1 when filesystem was
      145 * umounted, to 2 - when not */
      146 char s_magic[10]; /* reiserfs magic string indicates that
      147 * file system is reiserfs:
      148 * "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */
      149 __le16 s_fs_state; /* it is set to used by fsck to mark which
      150 * phase of rebuilding is done */
      151 __le32 s_hash_function_code; /* indicate, what hash function is being use
      152 * to sort names in a directory*/
      153 __le16 s_tree_height; /* height of disk tree */
      154 __le16 s_bmap_nr; /* amount of bitmap blocks needed to address
      155 * each block of file system */
      156 __le16 s_version; /* this field is only reliable on filesystem
      157 * with non-standard journal */
      158 __le16 s_reserved_for_journal; /* size in blocks of journal area on main
      159 * device, we need to keep after
      160 * making fs with non-standard journal */
161 } __attribute__ ((__packed__));
162
163 #define SB_SIZE_V1 (sizeof(struct reiserfs_super_block_v1))
164
165 /* this is the on disk super block */
166 struct reiserfs_super_block {
      167 struct reiserfs_super_block_v1 s_v1;
      168 __le32 s_inode_generation;
      169 __le32 s_flags; /* Right now used only by inode-attributes, if enabled */
      170 unsigned char s_uuid[16]; /* filesystem unique identifier */
      171 unsigned char s_label[16]; /* filesystem volume label */
      172 char s_unused[88]; /* zero filled by mkreiserfs and
      173 * reiserfs_convert_objectid_map_v1()
      174 * so any additions must be updated
      175 * there as well. */
176 } __attribute__ ((__packed__));


该结构定义中还包含了其他结构的定义,例如 journal_params,这是有关日志的一个结构,并非本文关注的重点,读者可以自行参考内核源代码中的 include/linux/ reiserfs_fs.h 文件。

实际上,超级块并不需要一个完整的数据块来存储,这个数据块中剩余的空间用来解决文件对象 id 的重用问题,详细内容请参看本系列文章下一部分的介绍。



B+ 树

与 ext2/ext3 的超级块比较一下会发现,reiserfs 的超级块中并没有索引节点表的信息,这是由于 reiserfs 并没有使用索引节点表,而是采用了 B+ 树来组织数据(在 reiserfs 的文档中也称为是 S+ 树)。图 2 中给出了一棵典型的 2 阶 B+ 树,其深度为 4。

图 2. 2 阶 B+ 树
北京联动北方科技有限公司

一棵 B+ 树有唯一一个根节点,其位置保存在超级块的 root_block 字段中。包含子树的节点都是中间节点(internal node),不包含子树的节点称为叶子节点(leaf node)。按照是否包含 B+ 树本身需要的信息,节点又可以分为两类,一类节点包含 B+ 树所需要的信息(例如指向数据块位置的指针,同时也包括文件数据),称为格式化节点(formatted node);另外一类只包含文件数据,而不包含格式化信息,称为未格式化节点(unformatted node 或 unfleaf)。因此,所有的中间节点都必须是格式化节点。一个格式化的叶子节点中可以包含多个条目(item,也称为项),所谓条目是一个数据容器,其内容可以保存到一个数据块中,也就是说,一个条目只能隶属于一个数据块,它是节点管理空间的基本单位。

为了方便理解起见,我们可以认为对于 B+ 树来说,一共包含 3 类节点:中间节点(其中保存了对叶子节点的索引信息)、叶子节点(包含一个或多个条目项)和数据节点(仅仅用来存放文件数据)。

熟悉数据结构的读者都会清楚,B+ 树是一棵平衡树,从根节点到达每个叶子节点的深度都是相同的。与 B- 树相比,B+ 树的好处是可以将数据全部保存到叶子节点中,而中间节点中并不存放真正的数据,仅仅用作对叶子节点的索引。为了方便起见,树的深度从叶子节点开始计算,叶子节点的深度为 1,之上的中间节点逐层加 1。在 B+ 树中查找匹配项首先要对关键字进行比较并沿对应指针进行遍历,直至搜索到叶子节点为止;而叶子节点也是按照关键字从小到大的顺序进行排列的。也正是由于这种结构,使得 B+ 树非常适合用来存储文件系统结构。



关键字

reiferfs 中采用的关键字包含 4 个部分,形式如下:

( directory-id, object-id, offset, type )



这 4 个部分分别表示父目录的 id、本对象的 id、本对象在整个对象(文件)中的偏移量以及类型。关键字的比较就是按照这 4 个部分逐一进行的。读者可能会好奇为什么不简单地采用对象 id 作为关键字。实际上,这种设计是有很多考虑的,采用 directory-id作为关键字的一部分,可以将相同目录中的文件和子目录组织在一起,加快对目录项的存取。offset 的出现是为了支持大文件,一个间接条目(后文中会介绍)最多能指向 (数据块大小-48)/4 个数据块来存放文件数据,在默认的 4KB 数据块中最大只能支持 4048KB 的文件。因此使用 offset 就可以表明该对象在文件中所处的偏移量。type 可以用来区分对象的类型。目目前reiserfs 支持四种类型,TYPE_STAT_DATA、TYPE_INDIRECT 1、TYPE_DIRECT 2、 TYPE_DIRENTRY,解释见后文的条目头部分。

在 3.5 之前的版本中,这 4 部分都是 32 位的整数,这样造成的问题是最大只能支持大约 232=4GB 的文件。从 3.6 版本开始,设计人员将 offset 扩充至 60 位,将 type 压缩至 4 位。这样理论上能够支持的最大文件就达到了 260 字节,但是由于其他一些限制,reiserfs 中可以支持的文件上限是 8TB。正是由于这个原因,reiserfs 中有两个版本的关键字,相关定义如清单 2 所示。

清单2. reiserfs 中与关键字有关的定义


363 struct offset_v1 {
       364 __le32 k_offset;
       365 __le32 k_uniqueness;
366 } __attribute__ ((__packed__));
367
368 struct offset_v2 {
       369 __le64 v;
370 } __attribute__ ((__packed__));
371
372 static inline __u16 offset_v2_k_type(const struct offset_v2 *v2)
373 {
       374 __u8 type = le64_to_cpu(v2->v) >> 60;
       375 return (type <= TYPE_MAXTYPE) ? type : TYPE_ANY;
376 }
377
378 static inline void set_offset_v2_k_type(struct offset_v2 *v2, int type)
379 {
       380 v2->v =
       381 (v2->v & cpu_to_le64(~0ULL >> 4)) | cpu_to_le64((__u64) type << 60);
382 }
383
384 static inline loff_t offset_v2_k_offset(const struct offset_v2 *v2)
385 {
       386 return le64_to_cpu(v2->v) & (~0ULL >> 4);
387 }
388
389 static inline void set_offset_v2_k_offset(struct offset_v2 *v2, loff_t offset)
390 {
       391 offset &= (~0ULL >> 4);
       392 v2->v = (v2->v & cpu_to_le64(15ULL << 60)) | cpu_to_le64(offset);
393 }
394
395 /* Key of an item determines its location in the S+tree, and
396 is composed of 4 components */
397 struct reiserfs_key {
       398 __le32 k_dir_id; /* packing locality: by default parent
       399 directory object id */
       400 __le32 k_objectid; /* object identifier */
       401 union {
             402 struct offset_v1 k_offset_v1;
             403 struct offset_v2 k_offset_v2;
       404 } __attribute__ ((__packed__)) u;
405 } __attribute__ ((__packed__));
406



尽管结构定义中并没有显式地声明 offset 和 type 分别是 60 位和 4 位长,但是从几个相关函数中可以清楚地看到这一点。



数据块头

在图 2 中我们曾经介绍过,b+ 树中的节点可以分为格式化节点和未格式化节点两种。未格式化节点中保存的全部是文件数据,而格式化节点中包含了 b+ 树本身需要的一些信息。为了与未格式化节点区分开来,每个格式化节点所占用的数据块最开头都使用一个数据块头来表示。数据块头大小为 24 个字节,定义如清单 3 所示。

清单3. block_head 结构定义


698 /* Header of a disk block. More precisely, header of a formatted leaf
699 or internal node, and not the header of an unformatted node. */
700 struct block_head {
       701 __le16 blk_level; /* Level of a block in the tree. */
       702 __le16 blk_nr_item; /* Number of keys/items in a block. */
       703 __le16 blk_free_space; /* Block free space in bytes. */
       704 __le16 blk_reserved;
       705 /* dump this in v4/planA */
       706 struct reiserfs_key blk_right_delim_key; /* kept only for compatibility */
707 };



block_head 结构中的 blk_level 表示该节点在 B+ 树中的层次,对于叶子节点来说,该值为 1;blk_nr_item 表示这个数据块中条目的个数;blk_free_space 表示这个数据块中的空闲磁盘空间。

格式化节点可以分为中间节点和叶子节点两类,它们所采用的存储结构是不同的。



中间节点

中间节点由数据块头、关键字和指针数组构成。中间节点中的关键字和指针数组都是按照从小到大的顺序依次存放的,它们在磁盘上的布局如图 3 所示。

图 3. 中间节点的布局
北京联动北方科技有限公司

每个关键字就是一个 16 字节的 reiserfs_key 结构,而指针则是一个 disk_child 结构,其大小为 8 个字节,定义如清单 4 所示。

清单4. disk_child 结构定义


1086 /* Disk child pointer: The pointer from an internal node of the tree
1087 to a node that is on disk. */
1088 struct disk_child {
      1089 __le32 dc_block_number; /* Disk child's block number. */
      1090 __le16 dc_size; /* Disk child's used space. */
      1091 __le16 dc_reserved;
1092 };



其中 dc_block_number 字段是所指向子节点所在的数据块块号,dc_size 表示这个数据块中已用空间的大小。对于一共有 n 个关键字的中间节点来说,第 i 个关键字位于 24+i*16 字节处,对应的指针位于 24+16*n+8*i 字节处。

需要注意的是,对于中间节点来说,数据块头的 blk_nr_item 字段表示的是关键字的个数,而指针数总是比关键字个数多 1,这是由 B+ 树的结构所决定的,小于 Key 0 的关键字可以在 Pointer 0 指针指向的数据块(下一层中间节点或叶子节点)中找到,而介于 Key 0 和 Key 1 之间的关键字则保存在 Pointer 1 指向的数据块中,依此类推。大于Key n的关键字可以在Pointer n+1中找到。



叶子节点

格式化叶子节点的结构比中间节点的结构稍微复杂一点。为了能够在一个格式化叶子节点中保存多个条目,reiserfs 采用了如图 4 所示的布局结构。

图 4. 格式化叶子节点的布局
北京联动北方科技有限公司

从图中可以看出,每个格式化叶子节点都以一个数据块头开始,然后是从两端向中间伸展的条目头和条目数据的数组,空闲空间保留在中间,这种设计是为了扩充方便。

所谓条目(item,或称为项)就是可以存储在单个节点中的一个数据容器,我们可以认为条目是由条目头和条目数据体组成的。

清单5. item_head 结构定义


460 /* Everything in the filesystem is stored as a set of items. The
461 item head contains the key of the item, its free space (for
462 indirect items) and specifies the location of the item itself
463 within the block. */
464
465 struct item_head {
       466 /* Everything in the tree is found by searching for it based on
       467 * its key.*/
       468 struct reiserfs_key ih_key;
       469 union {
             470 /* The free space in the last unformatted node of an
             471 indirect item if this is an indirect item. This
             472 equals 0xFFFF iff this is a direct item or stat data
             473 item. Note that the key, not this field, is used to
             474 determine the item type, and thus which field this
             475 union contains. */
             476 __le16 ih_free_space_reserved;
             477 /* Iff this is a directory item, this field equals the
             478 number of directory entries in the directory item. */
             479 __le16 ih_entry_count;
       480 } __attribute__ ((__packed__)) u;
       481 __le16 ih_item_len; /* total size of the item body */
       482 __le16 ih_item_location; /* an offset to the item body
       483 * within the block */
       484 __le16 ih_version; /* 0 for all old items, 2 for new
       485 ones. Highest bit is set by fsck
       486 temporary, cleaned after all
       487 done */
488 } __attribute__ ((__packed__));



从 item_head 结构定义中可以看出,关键字已经包含在其中了。ih_item_len 和 ih_item_location 分别表示对应条目的数据体的长度和在本块中的偏移量。请注意该结构的第 17、18 个字节是一个联合结构,对于不同类型的条目来说,该值的意义不同:对于 stat 数据条目(TYPE_STAT_DATA)或直接数据条目(TYPE_DIRECT),该值为 15;对于间接数据条目(TYPE_INDIRECT),该值表示最后一个未格式化数据块中的空闲空间;对于目录条目(TYPE_DIRENTRY),该值表示目录条目中目录项的个数。

目前 reiserfs 支持的条目类型有 4 种,它们是依靠关键字中的 type 字段来区分的;而在旧版本的关键字中,则是通过 uniqueness 字段来标识条目类型的,其定义如清单 6 所示。

清单6. reiserfs 支持的条目类型


346 //
347 // there are 5 item types currently
348 //
349 #define TYPE_STAT_DATA 0
350 #define TYPE_INDIRECT 1
351 #define TYPE_DIRECT 2
352 #define TYPE_DIRENTRY 3
353 #define TYPE_MAXTYPE 3
354 #define TYPE_ANY 15 // FIXME: comment is required
355

509 //
510 // in old version uniqueness field shows key type
511 //
512 #define V1_SD_UNIQUENESS 0
513 #define V1_INDIRECT_UNIQUENESS 0xfffffffe
514 #define V1_DIRECT_UNIQUENESS 0xffffffff
515 #define V1_DIRENTRY_UNIQUENESS 500
516 #define V1_ANY_UNIQUENESS 555 // FIXME: comment is required
517



下面让我们逐一来了解一下各种条目的存储结构。

STAT 条目

stat 数据(TYPE_STAT_DATA)非常类似于 ext2 中的索引节点,其中保存了诸如文件权限、MAC(modified、accessed、changed)时间信息等数据。在3.6 版本的 reiserfs 中,stat 数据使用一个stat_data 结构表示,该结构大小为 44 字节,其定义如清单 7 所示:

清单7. stat_data 结构定义


835 /* Stat Data on disk (reiserfs version of UFS disk inode minus the
836 address blocks) */
837 struct stat_data {
       838 __le16 sd_mode; /* file type, permissions */
       839 __le16 sd_attrs; /* persistent inode flags */
       840 __le32 sd_nlink; /* number of hard links */
       841 __le64 sd_size; /* file size */
       842 __le32 sd_uid; /* owner */
       843 __le32 sd_gid; /* group */
       844 __le32 sd_atime; /* time of last access */
       845 __le32 sd_mtime; /* time file was last modified */
       846 __le32 sd_ctime; /* time inode (stat data) was last changed */
       /* (except changes to sd_atime and sd_mtime) */
       847 __le32 sd_blocks;
       848 union {
             849 __le32 sd_rdev;
             850 __le32 sd_generation;
             851 //__le32 sd_first_direct_byte;
             852 /* first byte of file which is stored in a
             853 direct item: except that if it equals 1
             854 it is a symlink and if it equals
             855 ~(__u32)0 there is no direct item. The
             856 existence of this field really grates
             857 on me. Let's replace it with a macro
             858 based on sd_size and our tail
             859 suppression policy? */
       860 } __attribute__ ((__packed__)) u;
861 } __attribute__ ((__packed__));
862 //
863 // this is 44 bytes long
864 //



stat_data 条目使用的关键字中,offset 和 type 的值总是 0,这样就能确保 stat 数据是相同对象(object-id)中的第一个条目,从而能够加快访问速度。

与 ext2 的 ext2_indoe 结构对比一下就会发现,stat_data 中既没有记录数据块位置的地方,也没有记录删除时间,而这正是我们在 ext2/ext3 中恢复删除文件的基础,因此可以猜测得到,在reiserfs 文件系统中要想恢复已经删除的文件,难度会变得更大。

目录条目

目录条目中记录了目录项信息。目录条目由目录头和目录项数据(即文件或子目录名)组成。如果一个目录中包含的目录项太多,可以扩充到多个目录条目中存储。为了方便管理某个目录中子目录或文件的增减,目录条目也采用了与条目头类似的设计:从两端向中间扩充,其布局结构如图 5 所示。

图 5. 目录条目存储结构
北京联动北方科技有限公司

目录头是一个 reiserfs_de_head 结构,大小为 16 字节,其定义如清单 8 所示。

清单8. reiserfs_de_head 结构定义


920 /*
921 Q: How to get key of object pointed to by entry from entry?
922
923 A: Each directory entry has its header. This header has
deh_dir_id and deh_objectid fields, those are key
924 of object, entry points to */
925
926 /* NOT IMPLEMENTED:
927 Directory will someday contain stat data of object */
928
929 struct reiserfs_de_head {
       930 __le32 deh_offset; /* third component of the directory entry key */
       931 __le32 deh_dir_id; /* objectid of the parent directory of the object,
       932 that is referenced by directory entry */
       933 __le32 deh_objectid; /* objectid of the object, that is referenced */
       /* by directory entry */
       934 __le16 deh_location; /* offset of name in the whole item */
       935 __le16 deh_state; /* whether 1) entry contains stat data (for future),
       936 and 2) whether entry is hidden (unlinked) */
937 } __attribute__ ((__packed__));



reiserfs_de_head 结构中包含了 deh_dir_id 和 deh_objectid fields 这两个字段,它们就是其父目录关键字中对应的两个字段。deh_offset 的 7 到 30 位是文件名的 hash 值,0 到 6 位用来解决 hash 冲突的问题(reiserfs 中可以使用 3 种 hash 函数:tea、rupasov 和 r5,默认为 r5)。文件名的位置保存在 deh_location 字段中,而 deh_state 的第 2 位表示该目录条目是否是可见的(该位为 1 则表示该目录条目是可见的,为 0 表示不可见)。文件名是一个字符串,以空字符结束,按照 8 字节对齐。

直接条目与间接条目

在 reiserfs 中,文件数据可以通过两种方式进行存取:直接条目(direct item)和间接条目(indirect item)。对于小文件来说,文件数据本身和 stat 数据可以一起存储到叶子节点中,这种条目就称为直接条目。直接条目就采用图 4 所示的存储结构,不过每个条目数据体就是文件数据本身。对于大文件来说,单个叶子节点无法存储下所有数据,因此会将部分数据存储到未格式化数据块中,并通过间接条目中存储的指针来访问这些数据块。未格式化数据块都是整块使用的,最后一个未格式化数据块中可能会遗留一部分剩余空间,大小是由对应条目头的 ih_free_space_reserved 字段指定的。图 6 给出了间接条目的存储结构。

图 6. 间接条目存储结构
北京联动北方科技有限公司

对于缺省的 4096 字节的数据块来说,一个间接条目所能存储的数据最大可达 4048 KB(4096*(4096-48)/4 字节),更大的文件需要使用多个间接条目进行存储,它们之间的顺序是通过关键字中的 offset 进行标识的。

另外,文件末尾不足一个数据块的部分也可以像小文件一样存储到直接条目中,这种技术就称为尾部封装(tail packing)。在这种情况下,存储一个文件至少需要使用一个间接条目和一个直接条目。



实例分析

下面让我们来看一个实际的例子,以便了解 reiserfs 中的实际情况是什么样子。首先让我们来创建一个 reiserfs 文件系统,这需要在系统中安装 reiserfs-utils 包,其中包含的内容如清单 9 所示。

清单9. reiserfs-utils 包中包含的文件


[root@vmfc8 reiserfs]# rpm -ql reiserfs-utils
/sbin/debugreiserfs
/sbin/fsck.reiserfs
/sbin/mkfs.reiserfs
/sbin/mkreiserfs
/sbin/reiserfsck
/sbin/reiserfstune
/sbin/resize_reiserfs
/usr/share/doc/reiserfs-utils-3.6.19
/usr/share/doc/reiserfs-utils-3.6.19/README
/usr/share/man/man8/debugreiserfs.8.gz
/usr/share/man/man8/mkreiserfs.8.gz
/usr/share/man/man8/reiserfsck.8.gz
/usr/share/man/man8/reiserfstune.8.gz
/usr/share/man/man8/resize_reiserfs.8.gz



mkreiserfs 命令用来创建 reiserfs 文件系统,debugreiserfs 用来查看 reiserfs 文件系统的详细信息,如清单 10 所示。

清单10. 创建 reiserfs 文件系统


[root@ vmfc8 reiserfs]# echo y | mkreiserfs /dev/sda2

[root@vmfc8 reiserfs]# debugreiserfs -m /dev/sda2
debugreiserfs 3.6.19 (2003 www.namesys.com)

Filesystem state: consistent

Reiserfs super block in block 16 on 0x802 of format 3.6 with standard journal
Count of blocks on the device: 977952
Number of bitmaps: 30
Blocksize: 4096
Free blocks (count of blocks - used [journal, bitmaps, data, reserved] blocks): 969711
Root block: 8211
Filesystem is clean
Tree height: 2
Hash function used to sort names: "r5"
Objectid map size 2, max 972
Journal parameters:
Device [0x0]
Magic [0x28ec4899]
Size 8193 blocks (including 1 for journal header) (first block 18)
Max transaction length 1024 blocks
Max batch size 900 blocks
Max commit age 30
Blocks reserved by journal: 0
Fs state field: 0x0:
sb_version: 2
inode generation number: 0
UUID: 02e4b98a-bdf3-4654-9cae-89e38970f43c
LABEL:
Set flags in SB:
ATTRIBUTES CLEAN
Bitmap blocks are:
#0: block 17: Busy (0-8211) Free(8212-32767)
used 8212, free 24556
#1: block 32768: Busy (32768-32768) Free(32769-65535)
used 1, free 32767

#29: block 950272: Busy (950272-950272) Free(950273-977951) Busy(977952-983039)
used 5089, free 27679



从输出结果中可以看出,这是大约是一个 4GB 的分区,总共划分成 977952 个 4096B 大小的数据块;而超级块是第 16 个数据块(从 0 开始计算)。格式化过程中占用了其中的 8241 个数据块,其中从第 18 个数据块开始的 8193 个数据块用于日志(第 8210 数据块供日志头使用)。这个 B+ 树的根节点保存在该分区的第 8211 个数据块中。另外,数据块位图总共占用了 30 个数据块,-m 参数给出了这些数据块位图所占用的数据块的具体位置,这与前文中的介绍是完全吻合的。

我们真正关心的是存储实际数据的部分,从中可以了解在 reiserfs 中是如何对文件进行访问的。下面让我们来看一个实际文件系统的例子。

清单11. 分析 8211 数据块的内容


[root@vmfc8 reiserfs]# mount /dev/sda2 /tmp/test
[root@vmfc8 reiserfs]# dd if=/dev/sda2 of=block.8211 bs=4096 count=1 skip=8211
[root@vmfc8 reiserfs]# hexdump –C block.8211.hex



block.8211.hex 文件中就是根节点中的实际数据,图 7 给出了一个更为清晰的分析结果。

图 7. 新文件系统根节点的数据分析
北京联动北方科技有限公司

从图 7 中可以清楚地看出,这是一个格式化叶子节点(深度为 1),其中包含 4 个条目:两个是 STAT 条目,另外两个是目录条目。实际上,它们分别是当前目录和其父目录。继续分析就会发现,当前目录中只包含两个目录项:当前目录及其父目录,因此这是一个空目录。



小结

总体来说,reiserfs 所采用的设计很多都是专门针对如何充分提高空间利用率和改进小文件的访问速度。与 ext2/ext3 不同,reiserfs 并不以固定大小的块为单位给文件分配存储空间。相反,它会恰好分配文件大小的磁盘空间。另外,reiserfs 还包括了围绕文件末尾而专门设计的尾部封装方案,将文件名和文件数据(或部分数据)共同保存在 B+ 树的叶子节点中,而不会像 ext2 那样将数据单独保存在一个磁盘上的数据块中,然后使用一个指针指向这个数据块的位置。

这种设计会带来两个优点。首先,它可以极大地改进小文件的性能。由于文件数据和 stat_data(对应于 ext2 中的 inode)信息都是紧邻保存的,只需要一次磁盘 I/O 就可以将这些数据全部读出。其次,可以有效提高对磁盘空间的利用率。统计表明,reiserfs 采用这种设计之后,可以比相应的 ext2 文件系统多存储超过 6% 的数据。

不过,尾部封装的设计可能会对性能稍有影响,因为它每次文件发生修改时,都需要重新对尾部数据进行封装。由于这个原因,reiserfs 的尾部封装特性被设计为可以关闭的,这样就为管理员在性能和存储效率之间可以提供一个选择。对于性能非常关键的应用程序来说,管理员可以使用 notail 选项禁用这个特性,从而牺牲一部分磁盘空间来获得更好的性能。

与 ext2/ext3 相比,在处理小于 4KB 的文件时,reiserfs 的速度通常会快 10 到 15 倍。这对于新闻组、HTTP 缓存、邮件发送系统以及其他一些小文件性能非常重要的应用程序来说是非常有益的。

清单1. reiserfs_super_block 结构定义



赞(0)    操作        顶端 
谁是天蝎
注册用户
等级:大元帅
经验:90210
发帖:106
精华:0
注册:2011-7-21
状态:离线
发送短消息息给谁是天蝎 加好友    发送短消息息给谁是天蝎 发消息
发表于: IP:您无权察看 2011-8-5 14:26:12 | [全部帖] [楼主帖] 2  楼

北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司



赞(0)    操作        顶端 
总帖数
2
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论