Inode结构:操作inode的函式

发表于:2007-07-04来源:作者:点击数: 标签:
就跟super_block结构一样,每一个inode都有一个i_op的字段用来记录一组操做inode的函式。 struct inode_operations *i_op; 接下来,我们就来看看inode_operations结构里各个函式是做什么用的: · create(dir,dentry,mode) 当我们要产生一个新的档案时,Ker

  就跟super_block结构一样,每一个inode都有一个i_op的字段用来记录一组操做inode的函式。
  
  struct inode_operations *i_op;
  
  接下来,我们就来看看inode_operations结构里各个函式是做什么用的:
  
  · create(dir,dentry,mode)
  
  当我们要产生一个新的档案时,Kernel必须要先为这个档案产生一个inode,当然,配置inode内存这种事是属于VFS的工作范围,但是产生一个inode这件事跟档案系统本身有蛮大的关系,因此,VFS会呼叫档案系统里i_op->create()来做些额外的事,那i_op这个字段是打那儿来的呢? 因为一个档案一定是位于某个目录底下,所以,i_op这个字段就是从档案所在目录的inode里取出来的。而传给create()的dir就是那个目录的dentry指针,dentry则是我们要产生的档案的dentry (此时dentry已经配置好,但内部的数据却还没填入),而mode则是产生档案时所给的模式。我们知道Linux里有很多种的inode,有的是代表普通档案,有的则是代表目录,还有代表socket,pipe的。不同种类的inode其i_op所提供的函式都不尽相同,像一个普通的档案,我们根本不可能去呼叫它的create()函式,因为它不是目录,它没办法在目录底下产生一个inode。而像代表目录的inode就必须要提供create()才行,不然没办法在其底下产生子目录或档案。
  
  · lookup(dir,dentry)
  
  这个函式也是代表目录的inode所应该提供的。比方说我们有一个档案叫/usr/tmp/hello.txt,如果我们想读取这个档案的内容时,第一步就是要开启这个档案,如果要开启这个档案,我们首先就得先找到这个档案的inode。那Kernel是怎么找到它的inode的呢? 它会呼叫根目录的inode->i_op->lookup()找到/usr的dentry,则呼叫/usr目录的inode->i_op_lookup()找到/usr/tmp的dentry,接着再呼叫/usr/tmp的inode->i_op->lookup()找到/usr/tmp/hello.txt的dentry。而从它的dentry我们自然可以取得它的inode。而lookup()的用处就是从dir目录底下找到名称跟dentry指定的相同的档案dentry,基本上,这是属于档案系统应该做的事,VFS只负责帮你配置好dentry结构,并填入要找的文件名称。
  
  · link(old_dentry,dir,dentry)
  
  在Linux里,除了symbolic link之外,还有一种叫hard link的东西,symbolic link有它自己的inode,只是其内容指到别的档案的路径而已,但是hard link却是跟指到的档案共享一个inode,但是,hard link只能跟指到的档案位于同一个档案系统而已。当被指到的档案被删除时,只是你看不到那个档案而已,事实上,档案仍然是存在的,你可以使用之前建立的hard link来读取它。系统有提供一个叫ln的命令可以产生hard link,有兴趣的朋友可以试试看。而就programmer来讲,系统也提供了一个叫link()的系统呼叫来做hard link。link()在准备好一切之后,会呼叫i_op->link()去处理档案系统方面要做的事,i_op->link()至少应该要将inode->i_nlink的值加一才行。在i_op->link()的参数里,old_dentry是指被指到档案的dentry,dir是指我们所要产生的link所在目录的dentry,至于dentry则是要产生的link的dentry。这个函式是代表目录的inode所应该提供的。
  
  · unlink(dir,dentry)
  
  相信很多人都用过unlink()这个系统呼叫,这是用来将dir指到的目录底下的dentry档案删除掉。在真正删除之前,它会去检查dentry->d_inode->i_nlink是否归0,只有在nlink的值是0时才会删除。unlink()系统呼叫最后会呼叫i_op->unlink()去做档案系统额外要做的事,它至少应该把dentry->d_inode->i_nlink的值减一才对。跟i_op->link()一样,i_op->unlink()也是目录型别的inode所应提供的。
  
  · symlink(dir,dentry,symname)
  
  这个函式故名思义就是用来产生symbolic link用的。dir是symbolic link所在的目录dentry,symname则是symbolic link的内容,通常是个路径名称,至于dentry则是symbolic link本身的dentry。系统提供了一个symlink()的系统呼叫,就是用来做symbolic link的,它最后也是呼叫i_op->symlink()来处理。当然,每个档案系统内部要如何产生symbolic link的方式不尽相同,以ext2来讲,如果symname的长度小于60个byte的话,那在Ext2而言,这是一个fast symbolic link,因为路径名称就直接存在inode结构里,不用另外读取disk,所以,当i_op->symblink()被呼叫时,它的工作就是将路径名称加到inode里,但是如果大于等于60个byte,那就称为slow symbolic link,symname的内容会被放到disk上的block里,此时,i_op->symlink()就需要配置一个block存放symname的内容。这个函式也是目录型别的inode所应提供的,当然,如果你不想提供的话,也可以直接设成NULL。
  
  · mkdir(dir,dentry,mode)
  
  这个函式就是在产生一个目录时用,系统有提供mkdir()系统呼叫来产生目录,而这个系统呼叫最后会呼叫i_op->mkdir()来做底层的事情。dir是指我们要产生的目录所在的目录,至于dentry则是要产生的目录dentry,mode则是目录的权限。之前我们曾说过,每个inode->i_nlink记录了hard link的个数,而事实上,在代表目录的inode里,i_nlink的意义则跟它很像,它的意思是指目录里有几个档案或子目录,所以,每个目录刚产生时,它的i_nlink的都是2,因为,每个目录至少有二个子目录,分别是"."和".."。当产生完子目录之后,dir->d_inode->i_nlink的值也应该加1才对。如果inode是目录的话,那它应该提供这个函式才对。
  
  · rmdir(dir,dentry)
  
  i_op->rmdir()所做的事是跟mkdir()是相反的。跟mkdir()一样,i_op->rmdir()最后也会被rmdir()系统呼叫所使用。当VFS要呼叫rmdir()之前,它会先替我们把要删除的目录名称dentry找到,并把其父目录的dentry也找到,其中dir就是其父目录dentry,dentry就是指要删除的目录的dentry。当然,在呼叫i_op->rmdir()去删除目录时,VFS会先呼叫permission()并检查我们是否可以删除此目录并检查目录此时的状态,比方像目录是否现在被mount,是否为系统根目录,以及使用者要删除的是否为目录等等。所以,i_op->rmdir()要做的事就是纯粹检查目录是否为空的,是否目前还有别人在使用它,并做好删除目录的事情。如果inode是目录的话,那它应该提供这个函式才对。
  
  · mknod(dir,dentry,mode,rdev)
  
  在Linux里,也有一个命令是叫mknod。mknod命令主要是用来产生的special file,像是character device,block device,或fifo,socket之类的东西。同时,系统里也有一个系统呼叫mknod(),这个mknod()系统呼叫不尽可以产生特殊档案,也可以产生一般的档案,详情可见其man page。而事实上,mknod()系统呼叫最后也是呼叫档案系统的mknod()函式。mknod()系统呼叫会先检查user给的参数是否对,像是如果你指定要产生一个目录,VFS就先把你踢掉,除此之外,VFS还会先替你产生一个空的dentry用来放要产生的档案,当然,它也会检查user是否有权力产生这个档案,最后,它会把重头戏都交给i_op->mknod()去做。而i_op->mknod()要做什么呢? 当然,这部分是跟各个档案系统内部有关,基本上,这个函式需要在dir这个目录底下产生一个inode,其模式为mode,如果mknod()要产生的档案是device的话,那rdev就这个device的major number与minor number组合。除此之外,最重要的一件事就是要根据mode,在inode->i_op填入适当的值,比方说,如果产生一个character device,那inode->i->op应该指定一组操作character device的函式,如果产生的是普通档案的话,那inode->i_op也应填入操作普通档案inode的函式。这个函式对代表inode的目录而言,也是应该要提供的。
  
  · rename(old_dir,old_dentry,new_dir,new_dentry)
  
  这个函式是用来将位于old_dir里的old_dentry档案改名为new_dir里的new_dentry文件名称。档案系统所提供的rename()要做的事就是根据系统的implementation把改名字的事情做好,其它像是权限的检查在上层VFS会帮我们做好。要注意的是,old_dir->d_inode->i_nlink的值应该减一,而new_dir->d_inode->i_nlink的值则是应该加一。这个函式在Kernel里只有被系统呼叫rename()呼叫而已。有的人可能会以为这个函式应该由代表档案的inode提供,但事实上,这个函式必须要由代表目录的inode提供。理由就留给各位去想了。
  
  · readlink(dentry,buffer,buflen)
  
  只有当inode是代表一个symbolic link时,才需要提供这个函式,其它诸如档案或目录是不用提供这个函式的。这个函式的用处在于读取symbolic link的内容,也就是读取symbolic link指到的档案路径。跟上面其它的函式一样,这个函式最后也是会被系统呼叫readlink()所呼叫。至于readlink()要如何做是跟档案系统的implementation有关。像ext2,当档案路径的长度小于60个时,会直接从inode里读出资料,如果不是,则会读取disk上记录路径的block内容。dentry是代表symbolic link的inode,buffer是要路径存放的位置,至于buflen则是buffer的长度。
  
  · follow_link(dentry,base,follow)
  
  跟前一个函式一样,follow_link()这个函式只有symbolic link的inode需要提供。我们知道,当我们读到一个symbolic link叫a时,如果a指到/usr/hello.txt的话,那当我们读a时,事实上会读到/usr/hello.txt。这部分的工作就是由follow_link()完成的。这部分的转换就使用者的观点来看是不会感觉到的。在Linux里,并没有一个系统呼叫会呼叫follow_link()的。这个函式事实上是由lookup_dentry()呼叫do_follow_link(),再由do_follow_link()呼叫i_op->follow_link()。在Kernel里,寻找某个档案的inode是由namei(),再由它呼叫lookup_dentry()完成的,lookup_dentry()会由目录的最上层一层一层的找,如果找到的档案是symbolic link时,它最后会呼叫symbolic link的follow_link(),而follow_link()应该要读取所指到的档案路径,并且再呼叫lookup_dentry()去找这个档案,找到之后,再把它的dentry传回去。
  
  · readpage(file,page)
  
  在Linux里,每一个inode都代表一个档案或目录,而每一个档案在系统中则是由一个file结构所记录,readpage()就是将此file里的page内容读进来。基本上,VFS已经提供了一个readpage()的函式叫generic_readpage(),定义在,可以直接使用这个函式。
  
  · writepage(file,page)
  
  这个函式则是跟readpage()相反,是将page中的内容写回file里。但是,在VFS里并没有提供这样的一个函式可供使用,所以,如果有需要的话,需要自己提供。
  
  · bmap(inode,block)
  
  bmap()主要是用在做内存映像时用的。block是一个数字,它代表的是inode所代表的档案逻辑上的第几个block,bmap()负责将这个block的序号转换成disk上的区块序号。
  
  · truncate(inode)
  
  truncate()的作用就是用来将inode所代表的档案长度减小或增加,当然,详细的implementation是要依照系统而有所不同。至于最后的长度应该是多少,则是由VFS在呼叫i_op->truncate()之前将想要改变的长度填在inode->i_size里。
  
  · permission(inode,mask)
  
  故名思义,这个函式用来检查inode的权限,一般来讲,i_op->permission()在Kernel里并不会被直接呼叫,VFS提供一个也叫permission()函式,这个函式会去呼叫i_op->permission()。一般系统里如果要检查权限都是直接呼叫VFS提供的permission(),再由VFS的permission()去呼叫i_op->permission()。如果档案系统有提供i_op->permission()时,那就以i_op->permission()的结果为准。如果没有,就依照VFS的标准来做。mask的可以是MAY_READ,MAY_WRITE,和MAY_EXEC这三个值的OR组合。
  
  · smap(inode,sector)
  
  smap()的作用跟bmap()很像,但是,sector在这里指到是disk上的sector number。而不是逻辑上的block number。大部分的档案系统都没有提供这个函式,除了在umsdos有提供之外。
  
  · updatepage(file,page,offset)
  
  关于这个函式我也不太清楚,不过,可以知道的是,这个函式目前只有NFS档案系统有提供。有兴趣的朋友可以参考NFS的原始码。
  
  · revalidate(dentry)
  
  由于NFS有cache的问题,所以,这个函式主要也是在NFS中所使用的,为的是将dentry->i_node的内容做refresh。在里有一个函式叫do_revalidate()就会呼叫这个函式,很多系统呼叫像stat等都会呼叫do_revalidate()对inode做refresh。
  
  如果我们去看inode_operations结构的内容,就可以发现第一个字段是default_file_ops。其实这也是一组的函式,在Linux里,每一个档案都会有一个file结构来描述,而每一个file结构都会定义一组的函式来操作file结构,在inode里,也同时记录了用来操作inode所代表的档案的函式。在开始讲操作file结构的函式之前,让我们来看看file结构的内容。
  

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