Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42428
  • 博文数量: 16
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 135
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-28 13:50
文章分类
文章存档

2009年(16)

我的朋友

分类: LINUX

2009-12-21 17:19:06

目录项和超级块,节点的概念不同,它在设备上没有对应的磁盘数据结构。相反,它是目录文件的一部分。linux中目录也是一种文件,类型是'd'。但是目录中的内容和普通文件不同,它是由目录项组成的。注意目录项不是目录。我们经常用路径名执行相关操作,目录项就是为了查找方便的。
struct dirt_entry{
        unsigned short inode;
        char name[NAME_LEN];
};
可以看到一个目录项中包含了文件的i节点号和文件的名称。路径有两种,一种是绝对路径,一种是相对路径。比如/home/fish就是绝对路径,它以根目录 /为开头,而 X11/xorg.conf就是相对路径,它相对于目录/etc,也就是说后者的绝对路径是/etc/X11/xorg.conf。路径是由目录项组成的。拿 /home/fish/fish.c来说,第一个目录是/,即根目录。在根目录文件中有名字为home的目录项。然后通过home目录项中的i节点就可以找到home文件,通过i节点中的i_mode字段可知它是一个目录,在这个文件中有名为fish的目录项,然后根据这个目录项中的i节点号找到fish 文件。同样由fish的inode->i_mode知这也是个目录。同样在fish文件中找到名为fish.c的目录项,就可以找出fish.c的 i节点,搜索完成。
namei.c 是内核中最长的一个文件,它包含了许多关于目录的操作,如find_entry(从一个目录中搜索制定的目录项),add_entry(往指定的目录中添加一个文件目录项),get_dir(根据路径名找到指定的目录),dir_namei(返回指定目录的i节点指针),还有namei(根据路径名找到指定路径的i节点)等等。我们就先拿find_entry来看一下。
//dir:指定目录的i节点指针。name:文件名,namelen:文件名长度。
//返回:高速缓冲区指针和相应的目录项指针。
static struct buffer_head* find_entry(struct m_inode **dir,const char*name,int namelen,struct dir_entry **res_dir)
{
        int entries;
        int block ,i;
        struct buffer_head *bh;
        struct dir_entry *de;
        struct super_blocl *sb;
//如果定义了NO_TRUNCATE,则如果文件名大于最大长度NAME_LEN,返回。       
#ifdef NO_TRUNCATE
        if(namelen >NAME_LEN)
                return NULL;
#else
        if(namelen>NAME_LEN)
                namelen=NAME_LEN;
//计算本目录中的目录项数
        entries=(*dir)->i_size/(sizeof(struct dir_entry));
        *res_dir=NULL;
        if(!namelen)
                return NULL;
//如果是目录'..',对其进行特殊处理.
        if(namelen==2&&get_fs_byte(name)=='.'&&get_fs_byte(name+1)=='.'){
//如果本目录是进程的根目录,那么就将文件名修改为'.'.
                if((*dir)==current->root)
                        namelen=1;
//如果本目录是文件系统的根目录,那么取出文件系统的超级块
                else if((*dir)->i_num==ROOT_INO){
                        sb=get_super((*dir)->i_dev);
//如果本文件系统的安装节点存在,那么就释放原i节点,将*dir指向被安装到的i节点。对于根文件系统来说,就是它的根节点。
                        if(sb->s_imount){
                        iput(*dir);
                        (*dir)=sb->s_imount;
                        (*dir)->i_count++;
                        }
                }
        }
//如果第i节点指向的第一个直接块号为0,则返回
        if(!block=(*dir)->i_zone[0]))
                return NULL;
//读取节点坐在设备的目录项数据。也就是本目录文件的内容
        if(!(bh=bread((*dir)->i_dev,block)))
                return NULL;
//在bh中搜索指定的目录项
        i=0;
        de=(struct dir_entry*)bh->data;
        while(i//如果当前的文件块搜索完毕,则准备搜索下一块
        if((char*)de>=BLOCK_SIZE+bh->b_data){
                brelse(bh);
                bh=NULL;
//计算当前目录项在目录文件中的块数,并映射到设备逻辑块中
                if(!(block=bmp(*dir,i/DIR_ENTRIES_PER_BLOCK))||
//读取下一块
                   !(bh=bread((*dir)->i_dev,block))){
//如果下一目录项为空,则跳过该块,否则de指向该目录项,继续搜索
                        i+=DIR_ENTRIES_PER_BLOCK;
                        continue;
                }
                de=(struct dir_entry*)bh->b_data;
        }
//是否本目录项中的文件名字与指定的匹配?因为文件名称不再内核空间,所以不能使用strcmp,linux使用嵌入汇编。用cmpsb指令完成.
        if(match(namelen,name,de)){
                *res_dir=de;
                return bh;
        }
        de++;
        i++;
        }
//如果没有找到返回NULL
        brelse(bh);
        return NULL;
}
这下明白目录项的作用了吧。
add_entry也是如此,一直找到后本目录中的最后一个目录项,然后在后面使用已经申请的缓存块创建一个新的目录项。
get_dir 是找到指定路径中的最顶层目录的i节点。这里的路径必须是一个目录,且允许进入的。查找过过程是依次调用find_entry。如 /home/fish/fish.c路径,因为第一个字符为/,所以起始为根节点,否则就是当前目录current->pwd。然后搜索home目录项,找到后或却home的i节点,再搜索fish目录项。这样一直找到最顶的目录fish后返回目录节点。
dir_namei和get_dir相同,只不过它还返回指定的目录的名称和路径中文件名字长度。
namei是根据指定的路径找到其i节点,显然,它使用dir_namei和find_entry.dir_namei找到顶层目录后使用find_entry找到目录项的i节点号,再使用iget函数读取i节点。
struct m_inode*namei(const char * pathname)
{
        const char *basename;
        int inr,dev,namelen;
        struct m_inoe *dir;
        struct buffer_head*bh;
        struct dir_entry *de;
       
        if(!(dir=dir_namei(pathname,&namelen,&basename)))
                return NULL;
        if(!namelen)
                return dir;
        bh=find_entry(&dir,basename,namelen,&de);
        if(!bh){
                iput(dir);
                return NULL;
        }
//获取指定路径的i节点号
        inr=de->inode;
        dev=dir->i_dev;
        brelse(bh);
        iput(dir);
        dir=iget(dev,inr);
        if(dir){
                dir->i_atime=CURRENT_TIME;
                dir->i_dirt=1;
        }
        return dir;
}
剩下的就是一些关于目录的操作了,如打开文件,创建文件节点,建立目录,删除目录,建立连接等。到此为止,文件系统底层的一些机制就差不多了,在往上看,就是file了。file是打开的文件在内存中的表示,如果从进程的角度来看,首先看到的是file。file结构体包含了关于文件操作的内容,并将文件句柄和i节点建立管理。
struct file{
        unsigned short f_mode;
        unsigned short f_flags;
        unsigned short f_count;
        struct m_inode *f_inode;
        off_t f_pos; //读写偏移
}
文件的操作通过文件描述符属性判断文件的类型依次采用不同的方法,如块设备方法,字符设备方法,管道方法,正规文件等。
关于文件系统加载和卸载的函数有sys_umount,sys_mount和mount_root。在超级块中有字段sb->s_isup和sb- >s_imount,分别表示该超级块中被安装的i节点和安装到的i节点。显然对于根文件来说,二者是一样的,都是第一个根节点。i节点中有字段 inode->i_mount,表示该节点被安装了文件系统。所以文件系统的安装与卸载就是读取超级块,设置标志位以及释放超级块,清除标志位的过程。对于安装根文件系统来说还要设置进程的根目录为根节点。有了超级块和位图,就可以访问文件系统了。
在linux0.11中只支持minix1.0,关于文件的操作遍布于内核之中。在采用了vfs虚拟文件系统之后,各种文件系统可以通过这个统一的层被完美的支持。
阅读(915) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~