虚拟文件系统(VFS)简介:操作Superblock的函式

发表于:2007-07-04来源:作者:点击数: 标签:
super block里有一个字段是用来记录一组的函式,这个字段的型别是super_operations。这个结构在Kernel 2.2.1里包含了11个函式指针。这些指针是要让VFS来呼叫的。因此,这是VFS和档案系统之间的一个接口,经由这层接口,super block可以控制档案系统底下的档

  super block里有一个字段是用来记录一组的函式,这个字段的型别是super_operations。这个结构在Kernel 2.2.1里包含了11个函式指针。这些指针是要让VFS来呼叫的。因此,这是VFS和档案系统之间的一个接口,经由这层接口,super block可以控制档案系统底下的档案或目录。
  
  struct super_operations *s_op;
  
  在super_block结构里,s_op就是用来记录这一组的函式。这组函式必须由写档案系统的人来提供。底下我们就来看看super_operations里各个函式应该要提供什么样的功能。
  
  struct super_operations {
  void (*read_inode) (struct inode *);
  void (*write_inode) (struct inode *);
  void (*put_inode) (struct inode *);
  void (*delete_inode) (struct inode *);
  int (*notify_change) (struct dentry *,struct iattr *);
  void (*put_super) (struct super_block *);
  void (*write_super) (struct super_block *);
  int (*statfs) (struct super_block *,struct statfs *,int);
  int (*remount_fs) (struct super_block *,int *,char *);
  void (*clear_inode) (struct inode *);
  void (*umount_begin) (struct super_block *);
  };
  
  不知各位有没有发现,在这11个函式里,居然没有一个函式是用来读super block的。其实也没什么好奇怪的,因为读取super block的函式早在注册档案系统时就要给了,要不然,是谁把super block读出来。因此,我们可以发现super_operations里只有write_super(),put_super()等等,而没有read_super()。以下就分别来讨论各个函式应该提供什么样的功能,如果你想自己写一个档案系统,这部分可是很重要的喔。
  
  · write_super(sb)
  
  故名思义,这个函式主要就是用来将sb这个super block写到磁盘上的。在正常情况下,write_super()应该要检查sb->s_dirt是否为True,只有当s_dirt为True时才将super block写回disk里。当然,我想这部分主要还是看各个档案系统是如何的implement,但是,记得write_super()最后应该要将s_dirt设为0,表示这个super block不再是dirty。还有一件事,就是write_super()应该要检查档案系统是否被mount成只读(检查sb->s_flags&MS_RDONLY),或档案系统本身就是只读,像iso9660档案系统,在这种情况下,由于系统是只读,所以,系统设计者可以不提供write_super()或让write_super()不做事。
  
  · put_super(sb)
  
  当档案系统被umount时,VFS就会呼叫档案系统的put_super()。所以,put_super()要做的事就是将super block所配置的buffer释放掉。此外,一般来讲,如果档案系统是写成module的话,通常在put_super()也会呼叫MOD_DEC_USE_COUNT将module的reference count减一。至于MOD_INC_USE_COUNT则是应该在read_super()时做的。除此之外,有点要注意,有的人会认为put_super()应该要将sb释放掉,但是,根据Kernel 2.2.1版的原始码看来,这部分的工作是由VFS来做的。(所谓释放掉并不是呼叫kfree()将super block所占的内存释放,而是将sb->s_dev设成0而已。VFS会自动将s_dev为0的super block视为空的,而拿来重复使用。)
  
  · read_inode(inode)
  
  我想大家看名字就知道了,read_inode()就是去读一个inode,并将它放到传过来的inode结构里。在VFS里,read_inode()只会被get_new_inode()呼叫,而get_new_inode()又只会被iget()呼叫,所以,事实上,当我们去看Kernel的原始码的时候,我们不会看到直接呼叫read_inode()的情形出现,通常是呼叫iget()传回所要的inode。有的人会有疑问,read_inode()怎么知道要读那一个inode出来呢? 很简单,在呼叫read_inode()之前,VFS会先在inode结构里填入一些资料,以让read_inode()得知要读那个inode。在VFS里,它会填入以下的值:
  
  inode->i_sb = sb;
  inode->i_dev = sb->s_dev;
  inode->i_ino = ino;
  inode->i_flags = 0;
  inode->i_count = 1;
  inode->i_state = I_LOCK;
  
  当然,在你自己写的read_inode()里是不会用到这么多资料的,会用到的大概只有i_sb,i_no这两个而已。其实,就跟super block结构一样,inode结构也有一个字段是用来放档案系统自己认为需要的资料,这个字段通常也是在read_inode()中做的,除此之外,read_inode()最重要的一件事就是填入i_op这个字段,这个字段也是一组的函式,这组的函式是用来运作inode的。底下这些程序代码是从Ext2档案系统的ext2_read_inode()中取出的,它们就是用来填i_op这个字段。
  
  else if (S_ISREG(inode->i_mode))
  inode->i_op = &ext2_file_inode_operations;
  else if (S_ISDIR(inode->i_mode))
  inode->i_op = &ext2_dir_inode_operations;
  else if (S_ISLNK(inode->i_mode))
  inode->i_op = &ext2_symlink_inode_operations;
  else if (S_ISCHR(inode->i_mode))
  inode->i_op = &chrdev_inode_operations;
  else if (S_ISBLK(inode->i_mode))
  inode->i_op = &blkdev_inode_operations;
  
  · write_inode(inode)
  
  write_inode()要做的事就是将inode写回disk。
  
  · put_inode(inode)
  
  put_inode()是跟read_inode()是相对的。基本上,呼叫一次read_inode()就应该呼叫一次put_inode()。但是,在Kernel中,put_inode()跟read_inode()一样是不会直接被使用的。put_inode()只有在iput()中会被呼叫,iget()根据super block以及inode number以取得inode结构,而iput()则是iget()的相反,用iget()取得的inode应该用iput()释放掉。其实,inode结构里有一个i_count的字段,这是用来记录这个inode的reference count,所以,当我们取得inode时,应该对i_count加1,而在释放inode时则应该将i_count减1,但是,很幸运的,iget()和iput()已经帮我们做好这件事,我们不用在read_inode()和put_inode()中做了。那put_node()应该做什么呢? 它不用将inode中所配置的内存释放掉,因为这件事应该要在inode的reference count等于0而hard link的个数等于0的时候做,VFS会自动帮我们呼叫适当的函式。put_inode()所做的事是根据档案系统而有不同的需求,像在Ext2中就是将inode的prealloc的block释放掉。但切记,尽管呼叫put_inode()之后,inode->i_count不见得就会变成0,有可能有别的行程在使用这个inode,所以,put_inode()做的事不应该防碍别的行程对此inode的运作。
  
  · delete_inode(inode)
  
  delete_inode()做的事当然就是将inode删除掉啰。之前说过,当inode的reference count等于0时,VFS会开始将inode所占的内存释放掉。但是,如果这个时候hard link的个数也是0的时候,VFS就会呼叫delete_inode()将inode()从disk上删除掉。所以,user所提供的delete_inode()要做的事就是把disk上关于这个inode的资料以及档案系统自己本身所配置的东西删除掉。至于释放掉inode所占的内存则交由VFS来做吧。
  
  · clear_inode(inode)
  
  这个函式是用来将inode结构里的信息清除。档案系统应该只清除自己加在上面的资料,其余的部分应交由VFS来做。在Kernel里,是不会直接呼叫s_op->clear_inode()的,VFS提供一个函式也叫clear_inode(),它会呼叫s_op->clear_inode()。所以,如果有需要用到clear_inode()应该呼叫VFS提供的clear_inode()而不是s_op->clear_inode()。
  
  · statfs(sb,statfs,size)
  
  这个函式是用来取得档案系统的统计资料,statfs,fstatfs,和ustat这几个系统呼叫其实都是直接呼叫statfs来将传入的statfs结构填满,档案系统本身的统计资料本来就只有档案系统自己最清楚,所以,statfs()由super block来提供也是最为适当。
  
  · remount_fs(sb,flags,options)
  
  当一个档案系统已经被mount之后,如果我们想改变mount时所给予给的参数,可以执行mount这个命令,并在其-o参数后加入remount就可以了。基本上,remount所造成的参数改变VFS会帮我们做好,只是,为了怕参数的改变会对档案系统本身造成行为上的改变,所以,当user要求remount时,VFS会再呼叫叫s_op->remount_fs()以告诉档案系统user要改变mount的参数,如果档案系统本身有需要的话,可以在remount_fs()里做适当的调整,如果觉得不需要,那甚至可以不用提供这个函式让VFS使用。
  
  · umount_begin(sb)
  
  不知道各位有没有遇到过样的情形,当我们在某个档案系统中时,如果我们正在读写一个档案,可是由于某种不知名的原因,造成segmentation fault或是什么。结果当我们要将档案系统umount时,系统却告诉我们device is busy,所以无法umount。因此,新版的umount支持一个选项叫强迫性的umount,在上面这种没办法正常umount系统的情况下,就可以使用强迫性的umount。但是,事实上,VFS虽然有提供这样的功能,但是还是得要底层的档案系统支持才行。支持的方式是底层的档案系统要提供umount_begin()这个函式才行。要不然,尽管VFS支持,强迫性的umount仍然是做不到。老实说,umount_begin()要做什么我也不太清楚,因为好象没什么档案系统有提供这个函式。不过,根据VFS的原始码来看,它的工作应该是要把档案系统内部的state设回正常情况才对,其它的事就不用做了,交给VFS就对了。
  
  · notify_change(dentry,attr)
  
  在很多情况下,我们会对一个档案或目录的inode做出改变。比方说,我们可以对某个档案呼叫utime()改变这个档案的aclearcase/" target="_blank" >ccess time,或者是可以呼叫truncate()把档案的长度减短,这些系统呼叫都会改变档案的inode的属性。有的人会想到,那我们直接把inode拿来改改就好了嘛,何必提供这么个函式呢? 没错,VFS是的确把inode拿来改一改,但是,我们有说过,VFS做的事是属于全部档案系统所共同的部分,而档案系统之间的差异性,必须由各个档案系统提供函式来做。因此,万一使用者改了某个跟档案系统有很大关系的属性时,档案系统本身必须被告知才行。因此,notify_change()就是VFS告知档案系统的接口。跟上面很多函式一样,档案系统的notify_change()并不会直接被呼叫,s_op->notify_change()是被包装在VFS函式里,这个函式也叫notify_change(),它做的事就是把inode里的字段做user所要求的改变,并呼叫档案所属的档案系统的notify_change()。可以改变的属性是放在一个叫iattr结构里。
  
  struct iattr {
  unsigned int ia_valid;
  umode_t ia_mode;
  uid_t ia_uid;
  gid_t ia_gid;
  off_t ia_size;
  time_t ia_atime;
  time_t ia_mtime;
  time_t ia_ctime;
  unsigned int ia_attr_flags;
  };
  
  这个结构的宣告可以在里找到。ia_valid这个字段用来描述底下这几个字段那些是要改变的,ia_mode指的是新的权限,ia_uid为使用者id,ia_gid为群组id,ia_size是档案大小,ia_atime是access time,ia_mtime为modification time,ia_ctime是creation time,ia_valid的值可以是底下这几个常数的OR值。
  
  #define ATTR_MODE 1
  #define ATTR_UID 2
  #define ATTR_GID 4
  #define ATTR_SIZE 8
  #define ATTR_ATIME 16
  #define ATTR_MTIME 32
  #define ATTR_CTIME 64
  #define ATTR_ATIME_SET 128
  #define ATTR_MTIME_SET 256
  #define ATTR_FORCE 512
  #define ATTR_ATTR_FLAG 1024
  
  

原文转自:http://www.ltesting.net