ext2 源代码解析之 “从路径名到目标结点” (一)

来源:互联网 发布:熙然 内地网络男歌手 编辑:程序博客网 时间:2024/06/05 22:54


    两个主要函数,path_init和path_walk,他们结合在一起根据给定的文件路径名称在内存中找到或者建立代表着目标文件或目录的dentry和inode结构。注意,最终是信息是读取到内存中的。其中有个函数__user_walk()将path_init()和path_walk()包装在一起。本节中的所有代码在fs/namei.c中。

 

1.分析外包装“__user_walk()”

 

778/* 779 *      namei() 780  * 781  *is used by most simple commands to get the inode of a specified name. 782  *Open, link etc use their own routines, but this is enough for things 783  *like 'chmod' etc. 784  * 785  *namei exists in two versions: namei/lnamei. The only difference is 786  *that namei follows links, while lnamei does not. 787  *SMP-safe 788  */ 789int __user_walk(const char *name, unsigned flags, struct nameidata *nd) 790 { 791        char *tmp; 792        int err; 793 794        tmp = getname(name); 795         err = PTR_ERR(tmp);//即使没有出错,仍然会进行转化,所以下文要将err=0 796        if (!IS_ERR(tmp)) { 797                 err = 0; 798                 if (path_init(tmp, flags, nd)) 799                         err = path_walk(tmp,nd); 800                 putname(tmp); 801        } 802        return err; 803 }

 

1.1对函数参数的解释:

   name是用户空间的路径名称:flags内容是一些标志位,定义在/include/linux/fs.h,如下:

1323 /*1324 * The bitmask for a lookup event:1325 *  - follow links at the end1326 *  - require a directory1327 *  - ending slashes ok even fornonexistent files1328 *  - internal "there are morepath compnents" flag1329 */1330 #define LOOKUP_FOLLOW           (1)//symble link1331 #define LOOKUP_DIRECTORY        (2)// the target must be a directory1332 #define LOOKUP_CONTINUE         (4)1333 #define LOOKUP_POSITIVE         (8)1334 #define LOOKUP_PARENT           (16)1335 #define LOOKUP_NOALT            (32)


    注意第一项,符号链接可以跨越设备,而普通链接不可以。内核提供了不同的系统调用link和symlink,分别用于两种链接的建立关键数据结构.由于符号链接是可以跨越设备的,所以终点有可能悬空;而普通链接的终点是落实的。路径中包含符号链接的时候,是否继续往下搜索,规定如下:

  

82/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks: 83  *      inside the path - always follow.//路径内部 84  *      in the last component increation/removal/renaming - never follow.//创建,删除,重命名操作 85  *      if LOOKUP_FOLLOW passed - follow. 86  *      if the pathname has trailing slashes -follow. 87  *      otherwise - don't follow. 88  * (applied in that order). 89  */

     最后一个参数,是临时性的,仅仅用于存放返回结果。

 

655struct nameidata { 656        struct dentry *dentry; 657        struct vfsmount *mnt; 658        struct qstr last; 659        unsigned int flags; 660        int last_type; 661};

  dentry记录找到的内存中的dentry和inode信息,mnt记录文件系统安装信息,例如文件系统的安装点,根节点等。

 

总结:一个函数与外界的信息交互,在数学模型中形如y=f(x),其中形参(一般形参和指针)和全局变量是输入变量;函数返回值,全局变量和指针所指向的变量都是函数输出。对于不改变指向值的指针,往往需要用const关键字。


1.2 执行流程分析:


   getname()在系统空间分配一个页面,从用户空间将文件名复制到这个页面中,所以整个路径名字可以长达4K,由于这块空间是动态分配的,所以用完以后要通过putname释放。 

 

关于err的内联函数和解释如下:

1300 /*1301 * Kernel pointers have redundant information, so we can use a1302 * scheme where we can return either an error code or a dentry1303 * pointer with the same return value.1304 *1305 * This should be a per-architecture thing, to allow different1306 * error and pointer decisions.1307 */1308 static inline void *ERR_PTR(long error)1309 {1310        return (void *) error;1311 }13121313 static inline long PTR_ERR(const void*ptr)1314 {1315        return (long) ptr;1316 }13171318 static inline long IS_ERR(const void*ptr)1319 {1320        return (unsigned long)ptr > (unsigned long)-1000L;1321 }


注:函数返回值大于0xfffffc18的时候(就是-1000L),就说明出错了。


查看getname函数:

 129 char * getname(const char * filename) 130 { 131         char *tmp, *result; 132  133         result = ERR_PTR(-ENOMEM); 134         tmp = __getname(); 135         if (tmp)  { 136                 int retval = do_getname(filename, tmp); 137  138                 result = tmp; 139                 if (retval < 0) { 140                         putname(tmp); 141                         result = ERR_PTR(retval); 142                 } 143         } 144         return result; 145 }

 950 #define __getname()     kmem_cache_alloc(names_cachep, SLAB_KERNEL)
 951 #define putname(name)   kmem_cache_free(names_cachep, (void *)(name))

108 static inline int do_getname(const char *filename, char *page) 109 { 110         int retval; 111         unsigned long len = PATH_MAX + 1; 112  113         if ((unsigned long) filename >= TASK_SIZE) { 114                 if (!segment_eq(get_fs(), KERNEL_DS)) 115                         return -EFAULT; 116         } else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE) 117                 len = TASK_SIZE - (unsigned long) filename; 118  119         retval = strncpy_from_user((char *)page, filename, len); 120         if (retval > 0) { 121                 if (retval < len) 122                         return 0; 123                 return -ENAMETOOLONG; 124         } else if (!retval) 125                 retval = -ENOENT; 126         return retval; 127 }

 

129 char * getname(const char * filename) 130 { 131         char *tmp, *result; 132  133         result = ERR_PTR(-ENOMEM); 134         tmp = __getname(); 135         if (tmp)  { 136                 int retval = do_getname(filename, tmp); 137  138                 result = tmp; 139                 if (retval < 0) { 140                         putname(tmp); 141                         result = ERR_PTR(retval); 142                 } 143         } 144         return result; 145 }

 #define ENOMEM 12

 #define __getname()     kmem_cache_alloc(names_cachep, SLAB_KERNEL) 

涉及内存管理的部分,我们以后再来研究。


2 path_init函数(在fs/namei.c).


[path_init()]  /*SMP-safe */ 691int path_init(const char *name, unsigned int flags, struct nameidata *nd) 692 {// 这个函数的作用是:得到路径起点,设置了dentry和mnt两个变量,并进行初始化的相关设置 693        nd->last_type = LAST_ROOT; /* if there are only slashes... */ 694        nd->flags = flags; 695        if (*name=='/') 696                 return walk_init_root(name,nd); 697        read_lock(current->fs->lock);// 698        nd->mnt = mntget(current->fs->pwdmnt); 699        nd->dentry = dget(current->fs->pwd); 700         read_unlock(current->fs->lock);// 701        return 1; 702 }


疑问:flags从何而来?

 

2.1 nameidata结构中的last_type字段:


    首先将nameidata结构中的last_type字段设置成LAST_ROOT,这个字段可能的值定义在fs.h中,如下:

1336 /*1337 * Type of the last component on LOOKUP_PARENT1338 */1339 enum {LAST_NORM, LAST_ROOT, LAST_DOT,LAST_DOTDOT, LAST_BIND};

    在搜索过程中,这个字段的值会随着当前搜索结果而改变。例如,如果成功到了目标文件,它就变成LAST_NORM;如果停留在最后一个"."上面,则变成LAST_DOT.

2.2 其他字段的赋值

 

I)相对路径

 if后面的语句,是相对路径的情况:nd->dentry = dget(current->fs->pwd)这句代码有两层意思:1)将nameidata的dentry设置成当前工作目录的dentry结构,表示虚拟绝对路径中,这个节点之前的节点都已经解决了 2)这个具体的dentry结构多了一个用户,所以调用dget递增它的共享计数,即是dentry中d_count的值。关于,Vfsmount结构pwdmnt,可以参考以后的文件系统的安装和拆卸。

233 static __inline__ struct dentry *dget(struct dentry *dentry)234 {235        if (dentry) {236                 if(!atomic_read(&dentry->d_count))237                         BUG();//d_count==0,需要debug238                atomic_inc(&dentry->d_count);239        }240        return dentry;241 }

  #define atomic_read(v)          ((v)->counter) 

d_count 是atomic_t 类型

 typedef struct { volatile int counter; } atomic_t;

 #define atomic_inc(v) atomic_add(1,(v))

这一部分涉及内核的原子操作,具体原理可以参考linux内核原子操作

II)绝对路径


    if引导的是绝对路径下的情况,通过walk_init_root()从根节点开始查找(fs/namei.c):

[path_init() >>> walk_init_root()] 722/* SMP-safe */ 723static inline int 724walk_init_root(const char *name, struct nameidata *nd) 725 { 726        read_lock(current->fs->lock); 727         if (current->fs->altroot &&!(nd->flags & LOOKUP_NOALT)) { 728                 nd->mnt =mntget(current->fs->altrootmnt); 729                 nd->dentry =dget(current->fs->altroot); 730                read_unlock(¤t->fs->lock); 731                 if(__emul_lookup_dentry(name,nd)) 732                         return 0; 733                read_lock(¤t->fs->lock); 734        } 735        nd->mnt = mntget(current->fs->rootmnt); 736        nd->dentry = dget(current->fs->root); 737        read_unlock(currentt->fs->lock); 738        return 1; 739 }


 35 static inline struct vfsmount *mntget(struct vfsmount *mnt)

 36 {
 37         if (mnt)
 38                 atomic_inc(&mnt->mnt_count);
 39         return mnt;
 40 }


    代码解析:如果当前进程没有通过chroot设置自己的根目录,current->fs->altroot是0,所以nameidata中的两个指针就进行if之外的相关设置;反之,要查看当初调用path_init()的参数flags中的标志位LOOKUP_NOTLT是否为1.通常情况下,这个标志为0,所以如果current->fs->altroot是1,就会通过__emul_lookup_dentry(name,nd)将nameidata中的指针设置成指向“替换”根目录指针。因为linux 在i386处理器上不支持替换根目录,所以current->fs->altroot总是NULL,我们也不在此介绍相关内容,读者可以查看相关书籍。

 

    从path_init()成功返回的时候,nameidata结构中的指针dentry指向当前搜索路径的起点,接下来通过path_walk()顺着路径名的指引进行搜索。下面分段解析这个函数(fs/namei.c):

 

3.path_walk()函数解析

 

3.1)这个函数比较长(大概200行),我们进行分段解析,它的完整代码,我们可以查看附录部分。

442 /* 443  *Name resolution. 444  * 445  *This is the basic name resolution function, turning a pathname 446  *into the final dentry. 447  * 448  *We expect 'base' to be positive and a directory. 449  */450 int path_walk(const char * name, structnameidata *nd) 451 { 452        struct dentry *dentry; 453        struct inode *inode; 454        int err; 455        unsigned int lookup_flags = nd->flags; 456 457        while (*name=='/')// 跳过多个连续的“/” 458                 name++; 459        if (!*name)//name仅仅包含一个"/",则解析完成 460                 goto return_base; 461 462        inode = nd->dentry->d_inode;//注意dentry已经在init中设置完成 463         if (current->link_count)// current is the type of task_struct 464                 lookup_flags = LOOKUP_FOLLOW;

   link_count是task_struct的成员之一,用于防止链接导致的循环搜索。另外,因为path_walk()的起点是一个目录,所以一定有相应的inode存在,而不是空。

小结:以上代码跳过路径前面若干个“/”,并且给inode和flag初始化


3.2)接下来是对路径节点所作的for循环,我们将其拆开来看


466        /* At this point we know we have a real path component. */              /*beforethis point ,we only parse the / or the co*/ 467        for(;;) { 468                 unsigned long hash; 469                 struct qstr this; 470                 unsigned int c; 471 472                 err = permission(inode,MAY_EXEC);//检查对当前节点的访问权限,注意此处是可执行!!! 473                 dentry =ERR_PTR(err);//translate long err into pointer 474                 if (err) 475                        break; 476 477                 this.name = name; 478                 c = *(const unsigned char*)name;//name was a pointer to char,c is char name[0] >>> assci 479//#define init_name_hash() 0 480                 hash = init_name_hash(); 481                 do { 482                         name++; 483                         hash =partial_name_hash(c, hash);//对c和hash进行2维hash 484                         c = *(const unsignedchar *)name; 485                 } while (c && (c !='/')); 486                 this.len = name - (const char*) this.name; 487                 this.hash =end_name_hash(hash); 488     /* remove trailing slashes? */ 490                 if (!c) 491                         goto last_component; 492                 while (*++name == '/'); 493                 if (!*name) 494                         gotolast_with_slashes; 495

 40 /* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */
 41 static __inline__ unsigned long end_name_hash(unsigned long hash)
 42 {
 43         if (sizeof(hash) > sizeof(unsigned int))
 44                 hash += hash >> 4*sizeof(hash);
 45         return (unsigned int) hash;
 46 }

循环体中的局部变量this是一个qstr结构,用来存放路径名中当前节点的hash值和节点名的长度,定义在include/linux/dcache.h之中

 

struct qstr {       unsigned int hash;       unsigned int len;       const unsigned char *name;};


其中,481-485 就是逐个字符计算出当前节点的hash值(hash的过程是,给定一个raw和一个hash函数,返回一个hash值),具体函数如下:

/* partial hash update function. Assumeroughly 4 bits per character */ 34static __inline__ unsigned long partial_name_hash(unsigned long c, unsignedlong prevhash) 35 { 36        prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsignedlong)-4)); 37        return prevhash ^ c; 38 }最终的hash值,index的计算如下: 40 /*Finally: cut down the number of bits to a int value (and try to avoid losingbits) */ 41static __inline__ unsigned long end_name_hash(unsigned long hash) 42 { 43        if (sizeof(hash) > sizeof(unsigned int)) 44                 hash += hash >>4*sizeof(hash); 45        return (unsigned int) hash; 46 } 


路径名的分隔符是“\”,这里紧跟着当前路径名称的字符有两种可能

(1)是“/0”,表示结尾

(2)是“/”,这里又包含几种情况,/后面有字符路径和没有字符路径(比如ls usr/inlude/)

小结:以上代码,对当前的节点进行hash值的计算,并且将指向路径的指针移动到下一个节点所在的地方,如果节点解析完成,将进行跳转。

 

3.3)接下来查看当前节点

 

I)当前节点是. or ..,需要进行特殊处理

 

从前面的分析知道,当前节点一定是目录节点

496                 /* 497                  * "." and".." are special - ".." especially so because it has 498                  * to be able to know aboutthe current root directory and 499                  * parent relationships. 500                  */ 501                 if (this.name[0] == '.')switch (this.len) { 502                         default: 503                                 break;// is itfor the hiden file?no! if the node is directory, it cannot be named like .name 504                         case 2: 505                                 if(this.name[1] != '.') 506                                         break; 507                                follow_dotdot(nd);//如果当前就是“..”,那么需要向上跑到已经到达节点nd->dentry的父目录中去 508                                 inode =nd->dentry->d_inode; 509                                 /* fallthrough*/                                   //nomatter it is .. or ., we go to parse the next node 510                         case 1: 511                                 continue; 512                 }


下面,我们来看看 follow_dotdot(nd)

[path_walk >> follow_dotdot]

405 static inline void follow_dotdot(structnameidata *nd) 406 { 407        while(1) { 408                 struct vfsmount *parent; 409                 struct dentry *dentry; 410                read_lock(current->fs->lock); 411                 if (nd->dentry ==current->fs->root && 412                     nd->mnt ==current->fs->rootmnt)  {//nd指向的fsmnt结构是否代表根设备 413                         read_unlock(¤t->fs->lock); 414                         break; 415                 }//当前节点就是root节点,所以父节点还是本身,保持不变 416                read_unlock(¤t->fs->lock); 417                 spin_lock(&dcache_lock); 418                 if (nd->dentry != nd->mnt->mnt_root){//没有进行设备之间的跳跃 419                         dentry =dget(nd->dentry->d_parent);//完成了递增计数器和赋值 420                        spin_unlock(&dcache_lock); 421                         dput(nd->dentry); 422                         nd->dentry =dentry; 423                         break; 424                 }//下面是跨越了设备的情况  425                parent=nd->mnt->mnt_parent; 426                 if (parent == nd->mnt) { 427                        spin_unlock(&dcache_lock); 428                         break; 429                 } 430                 mntget(parent); 431                dentry=dget(nd->mnt->mnt_mountpoint); 432                 spin_unlock(&dcache_lock); 433                 dput(nd->dentry); 434                 nd->dentry = dentry; 435                 mntput(nd->mnt); 436                 nd->mnt = parent; 437        } 438        while (d_mountpoint(nd->dentry) &&__follow_down(&nd->mnt, &nd->dentry)) 439                 ; 440 } 441


这里分三种情况,

 第一种,已到达节点就是本进程的根节点,此时,保持不变。对应第一个if语句。

 第二种,已到达节点的nd->dentry和它的父节点在同一个设备上。这种情况下,父节点的dentry结构必然已经建立在内存中,而且dentry结构中的指针d_patent就指向其父节点,所以往上走一层是简单的事情。第二个if语句

 第三种,已经到达节点nd->dentry就是其所在设备的根节点,再往上就到达另外一个设备中。从文件系统的角度看,安装点和所安装设备的根目录是等价的,而目录树的根目录不等同于设备的根目录,我们在当前设备的根目录中,从这里往上一层,是目录树的上一层目录而不是安装点本身。当将一个存储设备“安装”到另外一个设备的某个节点的时候,内核会分配和设置一个fsmount结构,通过这个结构将两个设备和两个节点连接起来(参见后面“文件系统的安装和拆卸”)。所以,每个安装的设备都有一个fsmount结构,结构中有一个mnt_parent指向父设备,但是根设备的这个指针指向它自己,因为它已经没有父设备了;另外一个指针mnt_mountpoint, 指向代表着安装点的dentry。

 先检查当前Vfs结构是否代表着根设备;如果是的话,立即通过423行的break语句结束while循环;反之,进行相关处理。(这里没有解释清楚,我们在学习了下一节以后再回头看)。

  回到path_walk的代码之中,我们注意到“case2”没有break语句, 所以会通过case 1的continue语句回到for循环开始的地方执行,继续处理下一个节点。大多数节点不是以.命名的,我们来看看正常节点的处理流程。



我们来看以下dput:

  113  * @dentry: dentry to release  114  * 115  * Release a dentry. This will drop the usage count and if appropriate 116  * call the dentry unlink method as well as removing it from the queues and 117  * releasing its resources. If the parent dentries were scheduled for release 118  * they too may now get deleted. 119  * 120  * no dcache lock, please. 121  */ 123 void dput(struct dentry *dentry) 124 { 125         if (!dentry) 126                 return; 127  128 repeat: 129         if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock)) 130                 return; 131  132         /* dput on a free dentry? */ 133         if (!list_empty(&dentry->d_lru)) 134                 BUG(); 135         /* 136          * AV: ->d_delete() is _NOT_ allowed to block now. 137          */ 138         if (dentry->d_op && dentry->d_op->d_delete) { 139                 if (dentry->d_op->d_delete(dentry)) 140                         goto unhash_it; 141         } 142         /* Unreachable? Get rid of it */ 143         if (list_empty(&dentry->d_hash)) 144                 goto kill_it; 145         list_add(&dentry->d_lru, &dentry_unused); 146         dentry_stat.nr_unused++; 147         /* 148          * Update the timestamp 149          */ 150         dentry->d_reftime = jiffies; 151         spin_unlock(&dcache_lock); 152         return; 153  154 unhash_it: 155         list_del_init(&dentry->d_hash); 156  157 kill_it: { 158                 struct dentry *parent; 159                 list_del(&dentry->d_child); 160                 /* drops the lock, at that point nobody can reach this dentry */ 161                 dentry_iput(dentry); 162                 parent = dentry->d_parent; 163                 d_free(dentry); 164                 if (dentry == parent) 165                         return; 166                 dentry = parent; 167                 goto repeat; 168         } 169 }

 

子函数解析:

其中,以atomic开头的函数都是原子操作,这类函数的用法和意义,可以参考:linux内核部件分析(二)——原子性操作atomic_t

109 static __inline__ int list_empty(struct list_head *head)
110 {
111         return head->next == head;
112 }//kernel提供的判断链表是否非空的函数 

 

 void d_delete(struct dentry * dentry)
 

807 /* 808  * When a file is deleted, we have two options: 809  * - turn this dentry into a negative dentry 810  * - unhash this dentry and free it. 811  * 812  * Usually, we want to just turn this into 813  * a negative dentry, but if anybody else is 814  * currently using the dentry or the inode 815  * we can't do that and we fall back on removing 816  * it from the hash queues and waiting for 817  * it to be deleted later when it has no users 818  */ 819  820 /** 821  * d_delete - delete a dentry 822  * @dentry: The dentry to delete 823  * 824  * Turn the dentry into a negative dentry if possible, otherwise 825  * remove it from the hash queues so it can be deleted later 826  */ 827  828 void d_delete(struct dentry * dentry) 829 { 830         /* 831          * Are we the only user? 832          */ 833         spin_lock(&dcache_lock); 834         if (atomic_read(&dentry->d_count) == 1) { 835                 dentry_iput(dentry); 836                 return; 837         } 838         spin_unlock(&dcache_lock); 839  840         /* 841          * If not, just drop the dentry and let dput 842          * pick up the tab.. 843          */ 844         d_drop(dentry);}

static inline void dentry_iput(struct dentry * dentry)

74 /*  75  * Release the dentry's inode, using the fileystem  76  * d_iput() operation if defined.  77  * Called with dcache_lock held, drops it.  78  */  79 static inline void dentry_iput(struct dentry * dentry)  80 {//删除inode节点  81         struct inode *inode = dentry->d_inode;  82         if (inode) {  83                 dentry->d_inode = NULL;  84                 list_del_init(&dentry->d_alias);  85                 spin_unlock(&dcache_lock);  86                 if (dentry->d_op && dentry->d_op->d_iput)  87                         dentry->d_op->d_iput(dentry, inode);  88                 else  89                         iput(inode);  90         } else  91                 spin_unlock(&dcache_lock);  92 }

static __inline__ void d_drop(struct dentry * dentry)

122 /**123  * d_drop - drop a dentry124  * @dentry: dentry to drop125  *126  * d_drop() unhashes the entry from the parent127  * dentry hashes, so that it won't be found through128  * a VFS lookup any more. Note that this is different129  * from deleting the dentry - d_delete will try to130  * mark the dentry negative if possible, giving a131  * successful _negative_ lookup, while d_drop will132  * just make the cache lookup fail.133  *134  * d_drop() is used mainly for stuff that wants135  * to invalidate a dentry for some reason (NFS136  * timeouts or autofs deletes).137  */138 139 static __inline__ void d_drop(struct dentry * dentry)140 {141         spin_lock(&dcache_lock);142         list_del(&dentry->d_hash);143         INIT_LIST_HEAD(&dentry->d_hash);144         spin_unlock(&dcache_lock);145 }

 static inline void d_free(struct dentry *dentry)  

 63 /* no dcache_lock, please */  64 static inline void d_free(struct dentry *dentry)  65 {  66         if (dentry->d_op && dentry->d_op->d_release)  67                 dentry->d_op->d_release(dentry);  68         if (dname_external(dentry))  69                 kfree(dentry->d_name.name);  70         kmem_cache_free(dentry_cache, dentry);  71         dentry_stat.nr_dentry--;  72 }


II)当前节点是正常节点

 

/* 514                  * See if the low-levelfilesystem might want 515                  * to use its own hash.. 516                  */ 517                 if (nd->dentry->d_op&& nd->dentry->d_op->d_hash) { 518                         err =nd->dentry->d_op->d_hash(nd->dentry, &this);//使用自己的hash函数进行hash 519                         if (err < 0) 520                                 break; 521                 }//有些文件系统通过dentry_operations结构中指针d_hash提供专用的hash函数 522                 /* This does the actuallookups.. */ 523                 dentry =cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//在内存中寻找该节点已经建立的dentry结构 524                 if (!dentry) {//没有在内存中找到相应dentry 525                         dentry =real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);//在磁盘上进行寻找 526                         err = PTR_ERR(dentry); 527                         if (IS_ERR(dentry)) 528                                 break; } 530                 /* Check mountpoints.. */ 531                 while (d_mountpoint(dentry)&& __follow_down(&nd->mnt, &dentry)) 532                         ;//what? 533 534                 err = -ENOENT; 535                 inode = dentry->d_inode; 536                 if (!inode) 537                         goto out_dput; 538                 err = -ENOTDIR; 539                 if (!inode->i_op) 540                         goto out_dput; 541 542                 if(inode->i_op->follow_link) { 543                         err =do_follow_link(dentry, nd); 544                         dput(dentry);//删除对应dentry条目 545                         if (err) 546                                 gotoreturn_err; 547                         err = -ENOENT; 548                         inode =nd->dentry->d_inode; 549                        if (!inode) 550                                 break; 551                         err = -ENOTDIR; 552                         if (!inode->i_op) 553                                 break; 554                 } else { 555                        dput(nd->dentry); 556                         nd->dentry =dentry; 557                 } 558                 err = -ENOTDIR; 559                 if(!inode->i_op->lookup) 560                         break; 561                 continue; 562                 /* here ends the main loop */

       对当前节点的搜索是通过cached_lookup()&real_lookup()两个函数进行的。内核中有个杂凑表,dentry_hashtabel,是一个list_head指针数组,一旦在内存中建立目录节点的dentry结构,就根据其节点名称的hash值挂入hash表的某个队列。当路径名的某个节点变成path_walk()的当前节点时,位于其上游节点的dentry肯定都已经在内存的hash表中,如果在内存的相应结构中找不到相应节点的dentry结构,那么需要通过real_lookup()到磁盘目录寻找,然后载入内存。

 

      内存中还有一个队列dentry_unused,存放共享计数为0,不再使用的dentry结构(有时,也需要把还在使用的dentry结构强制脱链,强迫从磁盘上访问),已经脱链的那个数据结构由最后调用dput()使其共享计数变成0的进程负责将其释放。

 

    事实上,dentry中有6个list_head,d_vfsmnt,d_hash,d_lru,d_child,d_subdirs,d_alias.注意,list_head可以作为队列的头部,也可以将其所在的数据结构挂入到某个队列中。d_vfsmnt尽在dentry作为安装点的时候才使用,一个dentry一旦建立就通过d_hash挂入dentry_hashtable的某个队列里,当共享计数变成0的时候,通过d_lru挂入LRU队列dentry_unused之中,dentry通过d_child挂入父节点的d_subdir之中,同时通过d_patrent结构(这不是一个head_list)指向父目录的dentry结构,而它各自的子目录的dentry结构都在它的d_subdir队列中。

 

    一个dentry对应一个inode,但一个inode可能对应多个dentry。所以在inode中有个队列i_dentry,目录项功过dentry结构中的d_alias挂入相应的inode结构中的i_dentry队列。此外dentry结构中还有指针d_sb,指向其锁在设备超级快的super_block结构;指针d_op,指向特定系统的dentry_operations结构。


 (未完待续)

本文来源:谁不小心的csdn博客 ext2源码解析之“从路径名到目标节点”(一)

原创粉丝点击