Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1132135
  • 博文数量: 300
  • 博客积分: 37
  • 博客等级: 民兵
  • 技术积分: 772
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-26 04:46
文章分类
文章存档

2017年(4)

2016年(7)

2015年(19)

2014年(72)

2013年(71)

2012年(127)

分类: 服务器与存储

2013-04-15 16:43:34

    前面说过,虚拟文件系统VFS是对各种文件系统的一个抽象层,抽取其共性,以便对外提供统一管理接口,便于内核对不同种类的文件系统进行管理。那么首先我们得看一下对于一个具体的文件系统,我们该关注重点在哪里。

     对于存储设备(以硬盘为例)上的数据,可分为两部分:
     用户数据:存储用户实际数据的部分;
     管理数据:用于管理这些数据的部分,这部分我们通常叫它元数据(metadata)
     我们今天要讨论的就是这些元数据。这里有个概念首先需要明确一下:块设备。所谓块设备就是以块为基本读写单位的设备,支持缓冲和随机访问。每个文件系统提供的mk2fs.xx工具都支持在构建文件系统时由用户指定块大小,当然用户不指定时会有一个缺省值。在博文“硬盘的存储原理和内部架构”里我们提到过,硬盘的每个扇区512字节,而多个相邻的若干扇区就构成了一个簇,从文件系统的角度看这个簇对应的就是我们这里所说块。用户从上层下发的数据首先被缓存在块设备的缓存里,当写满一个块时数据才会被发给硬盘驱动程序将数据最终写到存储介质上。如果想将设备缓存中数据立即写到存储介质上可以通过sync命令来完成。上一篇博文里我们也说,块越大存储性能越好,但浪费比较严重;块越小空间利用率较高,但性能相对较低。如果你不是专业的“骨灰级”玩儿家,在存储设备上构建文件系统时,块大小就用默认值。通过命令“tune2fs -l /dev/sda1”可以查看该存储设备上文件系统所使用的块大小:

点击(此处)折叠或打开

  1. [root@localhost ~]# tune2fs -l /dev/sda1
  2. tune2fs 1.39 (29-May-2006)
  3. Filesystem volume name: /boot
  4. Last mounted on:
  5. Filesystem UUID: 6ade5e49-ddab-4bf1-9a45-a0a742995775
  6. Filesystem magic number: 0xEF53
  7. Filesystem revision #: 1 (dynamic)
  8. Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery sparse_super
  9. Default mount options: user_xattr acl
  10. Filesystem state: clean
  11. Errors behavior: Continue
  12. Filesystem OS type: Linux
  13. Inode count: 38152
  14. Block count: 152584
  15. Reserved block count: 7629
  16. Free blocks: 130852
  17. Free inodes: 38111
  18. First block: 1
  19. Block size: 1024
  20. Fragment size: 1024
  21. Reserved GDT blocks: 256
  22. Blocks per group: 8192
  23. Fragments per group: 8192
  24. Inodes per group: 2008
  25. Inode blocks per group: 251
  26. Filesystem created: Thu Dec 13 00:42:52 2012
  27. Last mount time: Tue Nov 20 10:35:28 2012
  28. Last write time: Tue Nov 20 10:35:28 2012
  29. Mount count: 12
  30. Maximum mount count: -1
  31. Last checked: Thu Dec 13 00:42:52 2012
  32. Check interval: 0 ()
  33. Reserved blocks uid: 0 (user root)
  34. Reserved blocks gid: 0 (group root)
  35. First inode: 11
  36. Inode size: 128
  37. Journal inode: 8
  38. Default directory hash: tea
  39. Directory Hash Seed: 72070587-1b60-42de-bd8b-a7b7eb7cbe63
  40. Journal backup: inode blocks
    该命令已经暴露了文件系统的很多信息,接下我们将详细分析它们。
     下图是我的虚拟机的情况,三块IDE的硬盘。容量分别是:

      hda: 37580963840/(1024*1024*1024)=35GB

      hdb: 8589934592/(1024*1024*1024)=8GB

      hdd: 8589934592/(1024*1024*1024)=8GB

     如果这是三块实际的物理硬盘的话,厂家所标称的容量就分别是37.5GB8.5GB8.5GB可能有些童鞋觉得虚拟机有点“假”,那么我就来看看实际硬盘到底是个啥样子。

     主角1:西部数据 500G SATA接口 CentOS 5.5

     实际容量:500107862016B = 465.7GB

     主角2:希捷 160G  SCSI接口 CentOS 5.5

     实际容量:160041885696B=149GB

    大家可以看到,VMware公司的水平还是相当不错的,虚拟硬盘和物理硬盘“根本”看不出差别,毕竟属于云平台基础架构支撑者的风云人物嘛。

   以硬盘/dev/hdd1为例,它是我新增的一块新盘,格式化成ext2后,根目录下只有一个lost+found目录,让我们来看一下它的布局情况,以此来开始我们的文件系统之旅。

     对于使用了ext2文件系统的分区来说,superblock的大小为1024字节,其实ext3superblock也是1024字节。下面的小程序可以证明这一点:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <linux/ext2_fs.h>
  3. #include <linux/ext3_fs.h>

  4. int main(int argc,char** argv){
  5.     printf("sizeof of ext2 superblock=%d\n",sizeof(struct ext2_super_block));
  6.     printf("sizeof of ext3 superblock=%d\n",sizeof(struct ext3_super_block));
  7.         return 0;
  8. }
     输出结果如下:

     硬盘的第一个字节是从0开始编号,所以第一个字节是byte0,以此类推。/dev/hdd1分区头部的1024个字节(byte0~byte1023)都用0填充,因为/dev/hdd1不是主引导盘。superblock是从byte1024开始,占1024B存储空间。我们用dd命令把superblock的信息提取出来:

点击(此处)折叠或打开

  1. dd if=/dev/hdd1 of=./hdd1sb bs=1024 skip=1 count=1
     上述命令将从/dev/hdd1分区的byte1024处开始,提取1024个字节的数据存储到当前目录下的hdd1sb文件里,该文件里就存储了我们superblock的所有信息,如下:

     上面的程序稍加改造,我们就可以以更直观的方式看到superblock的输出了:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <linux/ext2_fs.h>
  8. #include <linux/ext3_fs.h>

  9. int main(int argc,char** argv){
  10.     printf("sizeof of ext2 superblock=%d\n",sizeof(struct ext2_super_block));
  11.     printf("sizeof of ext3 superblock=%d\n",sizeof(struct ext3_super_block));
  12.     char buf[1024] = {0};
  13.     int fd = -1;
  14.     struct ext2_super_block hdd1sb;
  15.     memset(&hdd1sb,0,1024);

  16.     if(-1 == (fd=open("./hdd1sb",O_RDONLY,0777))){
  17.         printf("open file error!\n");
  18.         return 1;
  19.     }

  20.     if(-1 == read(fd,buf,1024)){
  21.         printf("read error!\n");
  22.         close(fd);
  23.         return 1;
  24.     }

  25.     memcpy((char*)&hdd1sb,buf,1024);
  26.     printf("inode count : %ld\n",hdd1sb.s_inodes_count);
  27.     printf("block count : %ld\n",hdd1sb.s_blocks_count);
  28.     printf("Reserved blocks count : %ld\n",hdd1sb.s_r_blocks_count);
  29.     printf("Free blocks count : %ld\n",hdd1sb.s_free_blocks_count);
  30.     printf("Free inodes count : %ld\n",hdd1sb.s_free_inodes_count);
  31.     printf("First Data Block : %ld\n",hdd1sb.s_first_data_block);
  32.     printf("Block size : %ld\n",1<<(hdd1sb.s_log_block_size+10));
  33.     printf("Fragment size : %ld\n",1<<(hdd1sb.s_log_frag_size+10));
  34.     printf("Blocks per group : %ld\n",hdd1sb.s_blocks_per_group);
  35.     printf("Fragments per group : %ld\n",hdd1sb.s_frags_per_group);
  36.     printf("Inodes per group : %ld\n",hdd1sb.s_inodes_per_group);
  37.     printf("Magic signature : 0x%x\n",hdd1sb.s_magic);
  38.     printf("size of inode structure : %d\n",hdd1sb.s_inode_size);
  39.     close(fd);
  40.     return 0;
  41. }
     打印结果如下:

     对于ext2/ext3文件系统来说数字签名Magic signature都是0xef53,如果不是那么它一定不是ext2/ext3文件系统。这里我们可以看到,我们的/dev/hdd1确实是ext2文件系统类型。hdd1中一共包含1048576inode节点(inode编号从1开始),每个inode节点大小为128字节,所有inode消耗的存储空间是1048576×128=128MB;总共包含2097065block,每个block大小为4096字节,每32768block组成一个group,所以一共有2097065/32768=63.99,即64group(group编号从0开始,即Group0Group63) 所以整个/dev/hdd1被划分成了64group,详情如下:

     用命令tune2fs可以验证我们之前的分析:

     再通过命令dumpe2fs /dev/hdd1的输出,可以得到我们关注如下部分:

     接下来以Group0为例,主superblockGroup0block0里,根据前面的分析,我们可以画出主superblockblock0中的位置如下:

     因为superblock是如此之重要,一旦它出错你的整个系统就玩儿完了,所以文件系统中会存在磁盘的多个不同位置会存在主superblock的备份副本,一旦系统出问题后还可以通过备份的superblock对文件系统进行修复。第一版ext2文件系统的实现里,每个Group里都存在一份superblock的副本,然而这样做的负面效果也是相当明显,那就是严重降低了磁盘的空间利用率。所以在后续ext2的实现代码中,选择用于备份superblockGroup组号的原则是3N5N7N其中N=0,1,2,3…。根据这个公式我们来计算一下/dev/hdd1中备份有supeblockGroup号:

     也就是说Group13579252749里都保存有superblock的拷贝,如下:

     用block号分别除以32768就得到了备份superblockGroup号,和我们在上面看到的结果一致。我们来看一下/dev/hdd1中Group和block的关系:

     从上图中我们可以清晰地看出在使用了ext2文件系统的分区上,包含有主superblockGroup、备份superblockGroup以及没有备份superblockGroup的布局情况。存储了superblockGroup中有一个组描述符(Group descriptors)紧跟在superblock所在的block后面,占一个block大小;同时还有个Reserved GDT跟在组描述符的后面。

     Reserved GDT的存在主要是支持ext2文件系统的resize功能,它有自己的inodedata block,这样一来如果文件系统动态增大,Reserved GDT就正好可以腾出一部分空间让Group descriptor向下扩展。

     未完,待续...

阅读(672) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~