您的位置:首页 > 其它

【文件管理】从路径名到目标节点

2014-05-23 19:13 218 查看
path_link()和path_walk()可以根据指定的文件路径名在内存中找到或建立代表着目标文件或目录的dentry结构和inode结构;它是很多系统调用的中间函数,如cddir(),chmod(),chown(),以及statfs()等;

(1)在__user_walk()中,name是指向用户空间的路径名,flags是一些标志位,如LOOKUP_DIRECTORY表示要寻找的目标必须是个目录,而LOOKUP_FOLLOW表示找到的目标只是个符号链接到其他文件或目录的一个一个目录项,则要沿着链接一直找到终点;symlink是针对符号链接的,可以跨设备,因此终点有可能悬空;link是针对普通链接的(不可跨设备),不可能悬空;若某个路径的中间节点是符号链接,那么总要follow,但是如果是最后一个节点是符号链接,则不用跟随;nameidata用来返回搜索的结果,成功返回时,其中的执政dnetry指向所找到的dentry结构,而在该dentry结构中则有指针指向相应的inode结构;指针mnt指向一个vfsmount数据结构,它记录着所属文件系统的安装信息(安装点,文件系统的根节点);先通过getname()在系统空间分配一个页面,并从用户空间把文件名复制到这个页面,因此真个路径名可达4k字节;由于这块空间是动态的,所以用完后要putname()将其释放掉;

(2)在path_init()中,首先将nameidata->last_type置为LAST_ROOT,这个字段会随着路径名的当前搜索结构而变,如成功找到了目标文件,则置为LAST_NORM,若停留在了一个'.'上,则置为LAST_DOT;如果路径名不是以'/'开头的,将nameidata的mnt和dentry指向以前tasklet_struct中fs的pwdmnt和pwd,并增加这两个计数;如果是'/'开头的,就要先通过walk_init_root()从根节点开始查找,如果当前进程没有使用alroot设置自己的替换根目录,则将nameidata的mnt和dentry指向以前tasklet_struct中fs的rootmnt和root,并增加这两个计数;如果当前进程使用alroot设置自己的替换根目录,以及LOOKUP_NOALT为0时,就将nameidata的mnt和dentry指向以前tasklet_struct中fs的alrootmnt和alroot,并增加这两个计数;这种替换根目录主要是为了适应Unix变种的,目前linux并不支持,故fs的alrootmnt和alroot总是NULL;

(3)从path_init()返回时,nameidata结构中的指针dentry指向路径搜索的起点,接着就通过path_walk()来沿着路径名的指引进行搜索了;若果路径名是/开头的,就先跳过它,若仅有'/',则可以返回了,已找到;在搜索的过程中有可能碰到一个节点只是指向另一个节点的连接,此时就用这个计数器对链的长度进行技术,主要是为了防止循环;若是符号链接,进入另一个设备的文件系统,有可能会递归的调用path_walk(),此时需将LOOKUP_FOLLOW设置成1;作为path_walk()起点的节点一定是个目录,一定要由相应的索引节点存在,所以指针inode一定是有效的;

(4)在对一个路径中的节点所做的for循环中,首先使用permission检查当前进程对当前节点的访问权限,注意对于中间节点所需的权限为执行权限即MAX_EXEC,权限不符,则返回出错码;qstr用来存放路径名中当前节点的杂凑值以及节点名的长度;若到'\0',说明当前节点已经是路径名的最后一节了,转入last_component;若是'/',可能是目录的最后一节,就转到last_with_slashes;若是当前节点为中间节点,会继续执行,此时一定是个目录,其中'.'表示是当期目录,‘..’当前目录的父目录,若name是以'.'打头的,判断qstr的len只能是1或2,若是'..'就要通过follow_dotbot()跑到nd->dentry的父目录去;

(5)在follow_dotbot()中,若nd->dentry是本进程的根节点,就不能往上再跑了,保持nd->dentry不变;若nd->dentry与其父节点在同一设备上,则直接返回nd->dentry->d_parent;若nd->dentry与其父节点不在同一设备上,(当一个存储设备安装到另一个设备上的某个结点,内核会分配和设置一个vfsmount结构,通过这个结构将两个设备以及两个节点连接起来),我们得跑到安装点的上一层目录(安装点是与当前目录等效的),先检查当前的vfsmount结构是否代表着根设备,使得话也就结束循环了,这样nameidata中dentry和mnt两个指针保持不变,相当于在根目录中打上命令'cd
..';反之,当前设备不是跟设备,那就把nameidata中dentry和mnt设置成指向上层设备的vfsmount结构,以及该设备上的安装点的上一层目录,然后回到循环处;

(6)在path_walk()中,有些文件系统有自己的杂凑函数,所以再一次计算当前节点的杂凑值;对当前的节点搜索是通过cached_lookup()和real_lookup()两个函数进行的,先通过cached_lookup()寻找到该节点已建立的dentry结构;内核中有个杂凑表dentry_hashtable,是个list_head指针数组,一旦内存中建立起一个目录节点对的dentry,就根据节点名的杂凑值挂入杂凑表中的某个队列,位于其上游的所有节点必定在dnetry中了,而当前节点或其下一个节点不一定在,如果在内存中没有找到当前节点的dentry,那么就要通过real_lookup到磁盘上通过其所在的目录寻找,找到后再内存中建立起dentry并挂入到杂凑表中的某个队列;内核中凡是已经没有用户,共享计数为0的dentry结构就通过结构中另一个list_head挂入dentry_unused队列,这是一个LRU队列,可回收,当然有时也不放入该队列,直接由dput使其计数变为0的进程负责释放掉;dentry有6个list_head,d_vfsmount尽在该dentry为一个安装点时才使用;dentry通过d_child挂入其父节点的d_subdirs目录下,同时又通过指针d_parent指向其父目录dentry;而他各个子目录的dnetry又在d_subdirs队列中;d_hash,d_lru已介绍;

(7)一个有效的dentry必定有一个相应的inode结构,但一个inode可能却对应着不止一个dentry结构;因为连接的存在,但还是代表这个文件的所有目录项都挂入d_alias队列中;dentry还有个指针指向所在设备的超级块的super_block,以d_op指向特定文件系统的dentry_operations;还有一个队列头d_vfsmnt,用于文件系统的安装;

(8)在cached_lookup()中,显示通过d_lookup()在杂凑表中寻找;先调用d_hash来深度杂凑可得到队列头,然后在d_lookup()线比较通过杂凑值,父亲相同,再到比较文件名,若没有dentry_operatons提供比较函数,就使用memcmp来比较,找不到就为NULL;对于找到的dentry还要有d_invalidate()来验证,验证失败,还要将其从杂凑表中离开队列(如网络系统中长时间未访问的dentry);

(9)在path_walk()中,dentry不在杂凑队列中,或无效,就返回了NULL,那就要通过real_lookup从父目录在磁盘上的内容中找到本节点的目录项,再根据其内容在内存中建立起一个dentry;在real_lookup()中,建立dentry结构的过程是不允许其他进程干扰的,所以通过信号量放在临界区中进行;为防止重复建立这个dentry,还需要通过d_lookup()确认一下所需的dentry确实不在杂凑队列中;使用d_alloc()建立并初始化一个dentry结构,可以餐厨是通过slab机制来分配的,若文件名较短时,就使用dentry->d_iname,否则使用kmalloc来分配,但是d_name.name始终指向这个str;初始化上述的6个队列头,以及其他初始化;分配了空间后就要从磁盘上由父节点代表的那个目录中寻找当前节点的目录项并设置结构中的其他信息;如果寻找失败。还要适应dput()来撤销已分配空间的dentry();从磁盘上寻找的过程因文件系统的不同而不同,通过父节点的inode中i_op找到对应的inode_operations,通常代表目录和文件的inode的inode_operaions是不同的;对于Ext2,其目录节点的函数跳转结构为ext2_dir_inode_operaions,对于ext2_lookup()中,先由ext2_find_entry()从磁盘上找到并读入当前节点的目录项,然后通过iget()根据索引节点号从磁盘上读入相应的索引节点并在内存中建立起对应的inode结构,最后由d_add完成dentry结构的设置并挂入杂凑表的某个队列;

(10)在ext2_find_entry()中,先预读得到好几个记录块,然后找到对应的目录项,其他未用的记录块要释放掉,最后返回的对应的记录块;

(11)在ext2_lookup()中,通过iget()来根据查得到的父节点的索引节点号来找到或建立所需的inode结构;iget是通过iget4来查找的;在iget4(),同样目标节点的inode也有可能在内存中,也有可能从磁盘上读入其索引节点后在内存中创建,inode也有个杂凑表inode_hashtable,inode通过i_hash挂入到该杂凑队列的某个队列,于是先通过find_inode()在杂凑队列中寻找,找到后就通过iget()递增该共享计数,由于索引节点号在同一设备上是唯一的,在杂凑计算时要把所在设备的super_block结构的地址也结合进去;若找不到,就用get_new_inode()从磁盘上读入相应的索引节点并建立起一个inode结构;在get_new_inode()中,主要是分配建立和初始化,其中i_dev表示的这个结构所代表的文件所在的设备,可以看出它的值是来自super_block的,对于索引节点的读入是使用super_operations中的函数指针read_inode提供的,即ext2_read_inode();在ext2_read_inode()中,先读记录块组,和节点组内偏移,然后再读出记录块号,读出的是ext2_inode的原始数据,然后将ext2_inode赋给内存中inode,特别注意的是inode的ext2_inode_info中i_data存储的是一些指针,(普通文件或内容)直接或间接的指向磁盘上存储着该文件内容的所在记录块
;若是符号链接,直接存储链接目标的路径名;然后再完成具体文件系统和VFS之间的连接,也就是设置一些inode_operations,fileoperations,以及address_space的指针(常规文件才设置);找到了inode,最后使用d_add()将它挂入杂凑表中的某个队列,先是使用d_instantiate(),inode和dentry是一对多的关系,因此inode是个队列,dentry结构通过其队列头d_alias挂入在相应的inode队列中,最后将dentry挂入到杂凑队列中;(dentry有个指针指向d_inode,一对多的关系);

(12)现在回到real_lookup()中,当前节点dentry是有了,但还是有可能是个安装点,使用d_mountpoint()来加以检验,如果是,就调用__follow_down()前进到所安装设备的根节点;如果当前节点是一个链接,对于提供链接的文件系统,就通过do_follow_link来处理,否则就已经找到了所需的节点了,只需通过dput()来递增dentry结构的共享计数,因为path_walk()不再需要使用这个结构了,在do_follow_link()中,就以前保存砸i_data中的链接目标名,调用vfs_follow_link(),就意味着从较低层次回到了更高的vfs层,因为符号链接的目标有可能在另一个格式不同的文件系统中,可想而知又要调用path_walk()了,这样对于中间节点的搜索过程也就完成了;

(13)在path_walk()继续循环,如果到了最后一个节点时,转入last_component('\0'路径名的最后一节了);转到last_with_slashes(若是'/',可能是目录的最后一节);到了last_with_slashes时,先要设置flags成为LOOKUP_FOLLOW和LOOKUP_DIRECTORY,代表着这个节点若果是一个连接,一定要前进到所连接的对象;如果LOOKUP_PARENT是1,说明寻找的并不是路径中的终点,还是它的上一层,所以到了lookup_parent处,根据终点的,将nameidata中的last_type设置成LAST_NORM或LAST_DOT,或LAST_DOTDOT;

(14)终结结点和中间节点的要求是不一样的,它并不需要一个inode挂钩,而中间节点必须有,但是LOOKUP_POSITIVE表示终结节点必须要有inode,否则返回时会报错;对于终结结点,do_follow_link()仅当LOOKUP_FOLLOW为1时才调用;对于终结结点,最后一层目录搜索时,仅当LOOKUP_DIRECTORY才会意味着失败;

(15)从path_walk()返回时,函数值为0,表示搜索成功,此时nameidata指针dentry指向了目标节点(不一定是终结节点),mnt指向目标节点所在设备的安装结构,last_type表示最后一个节点类型,节点名在last中;如果失败的话,则函数值一定是负的出错代码,nameidata则提供失败节点的节点名信息;

系统调用cddir()的代码在sys_cddir()中,先用permission()检查访问权限,set_fs_pwd()将当前进程的fs_struct结构的pwd和pwdmnt分别设置成由nameidata安装的dentry指针和vfsmount指针;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: