By cszhao1980
我们现在对inode有了一定的了解,正如前面所说,inode记录的信息比较靠近文件的物
理存储。那末,文件系统的树状结构是如何实现的呢?
答案就是“目录”。首先需要强调的是,目录也是一种文件,它也拥有与普通文件相同
的inode结构。所不同的是其文件内容——逻辑上,我们可以把目录文件的内容看作一个
“目录项数组”,它由若干“目录项”构成,一个目录项对应于该目录内的一个文件,
共16个words(显然,1个块可容纳32个目录项),包含1个1 6位“i n o d e”表指针,以及
一个1 4个字符组成的名字。显然,inode指针即指向该文件的inode,而名字即该文件(或
目录)的名字。如果您对unix比较熟悉的话,您会知道,一个目录中比如含有的两个目录项:
当前目录(“.”)和父目录(“..”)。通过这种方式,目录就形成了文件系统树的一个结点,
从而形成了文件系统的树状结构。
而目录的inode结构中的i_size1记录的就是其文件总长度,即目录项总长度,而目录项的
个数 = i_size1/16。
显然,当新建一个文件时,需要在其父目录的目录项中添加一项;而删除文件时,需要从
其父目录的目录项里将该文件的目录项删除——删除时,只需将inode指针置0即可(这些
目录项被称为空目录项,可以用于分配)。
namei(func, flag)用来搜索目录,返回执行文件的inode指针,它有两个参数:
1. func:一个函数指针,每次调用该函数都将得到一个char,通过多次调用可获得一个字符串,而
该字符串为一个“路径名”;
2. flag:搜索目的
(1)0:“搜索”;
(2)1:请求在父目录项中添加该文件的目录项;
(3)2:请求在父目录中删除该文件的目录项。
对不同的搜索目的,namei的处理也有不同,比如,对一般的搜索目的,用户只需要对路径中每
个节点目录拥有“EXEC”权限,而“添加”、“删除”目的,则还需要在父目录中拥有“WRITE”权限。
namei的实现透露了很多有趣的信息:
1. 进程的u结构里定义了很多相关的结构,如:
(1) u_cdir——指向“当前目录”的inode结构;
(2) u_dbuf[DIRSIZ]——用来暂存读到的文件(目录)名;
(3) u_offset[0~1]——记录当前读到的文件位置(偏移),单位是byte。由两个word模拟一个32位
的偏移量,u_offset[0]为高16位,u_offset[1]为低16位;
(4) u_dirp和u_dent用来暂存搜索到的路径结点:
0430: char *u_dirp;
0431: struct {
0432: int u_ino; // inode指针
0433: char u_name[DIRSIZ]; //文件名
0434: } u_dent;
即如果成功匹配,则u_dent中记录的是指定文件在其父目录里的目录项。
2. 访问一个文件的inode的典型方法如下:
(1)从根目录(或当前目录)出发,读取其内容;
(2)搜索其目录项,匹配路径的下个结点名字,获取该结点的inode;
(3)继续向下搜寻,直到获取最终文件的inode。
3. 当搜索目的是“删除文件”时:
匹配成功的话,返回值为父目录的inode。
4. 当搜索目的是“添加文件”时:
(1)如果在父目录中已经有同名的文件,则返回该文件的inode;
(2)否则,表示可以添加,则返回NULL,且
1) u_pdir设置为父目录的inode;
2) u_offset[1]设置为文件内容的displacement,可以作为目录项起始地址来设置新加目录项;
其代码比较令人费解,如下所示:
7603: if(flag==1 && c=='\0') {
7604: if(access(dp, IWRITE))
7605: goto out;
7606: u.u_pdir = dp;
7607: if(eo)
7608: u.u_offset[1] = eo-DIRSIZ-2; else
7609: dp->i_flag =| IUPD;
7610: return(NULL);
难懂的是7609行:
i. 为什么不设置u_offset[1]?
ii. 父目录inode为何要设置IUPD标志?
很简单,当eo为0时,表示此时没有空目录项可用,所以需要扩大父目录的“目录项数组”。
i. 此时,offset[1]其实正好指向了“目录项数组”后面可以用于分配新目录项的位置;
ii. 父目录需要扩大“目录项数组”,即文件需要修改,故要设置IUPD标志。
博客地址:http://blog.csdn.net/cszhao1980
博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html