Chinaunix首页 | 论坛 | 博客
  • 博客访问: 439505
  • 博文数量: 123
  • 博客积分: 2686
  • 博客等级: 少校
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-23 22:11
文章分类
文章存档

2012年(3)

2011年(10)

2010年(100)

2009年(10)

我的朋友

分类: LINUX

2010-02-27 16:56:11

内核版本:linux-2.6.32.2
 
一、虚拟文件系统的原理
虚拟文件系统简称VFS,单纯用文字和图描述VFS的原理是没有意义的,直接看源代码吧,看看系统调用open是怎样打开一个文件的,做是哪些工作,与VFS有什么关联,与具体的文件系统有什么关联,VFS与具体的文件系统是如何衔接的。
在内核2.6.32系统调用open定义在fs/open.c中,且用宏SYSCALL_DEFINE3定义,不同以前版本。
 
STEP 1:open系统调用的定义
 

//等价于void open(const char __user *filename,int flags,int mode)

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
    long ret;

    if (force_o_largefile())
        flags |= O_LARGEFILE;
//调用do_sys_open 查找filename路径对应的物理索引节点,索引节点包含了文件系

统处理文件所需要的所有信息,在内存中的索引节点对象由一个inode数据结构组成
    ret = do_sys_open(AT_FDCWD, filename, flags, mode);
    /* avoid REGPARM breakage on x86: */
    asmlinkage_protect(3, ret, filename, flags, mode);
    return ret;
}

STEP 2:do_sys_open的代码

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
    char *tmp = getname(filename);
    int fd = PTR_ERR(tmp);

    if (!IS_ERR(tmp)) {
        fd = get_unused_fd_flags(flags);//获取没有使用的数组索引,可知文件描述符实质是进程打开文件列表中对应某个文件对象的索引值
        if (fd >= 0) {

//调用do_filp_open函数返回一个file对象f,代表由该进程打开的一个文件;进程通过这样的一个数据结构对物理文件进行读写操作
           struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
            if (IS_ERR(f)) {
                put_unused_fd(fd);
                fd = PTR_ERR(f);
            } else {
                fsnotify_open(f->f_path.dentry);
                fd_install(fd, f);//fd_install()建立文件描述符fd与file对象f的联系,以后进程对文件的读写都是通过操纵该文件描述符而进行
            }
        }
        putname(tmp);
    }
    return fd;
}


STEP 3:do_filp_open做了什么事情?do_filp_open代码较长,只贴关键部分。

struct file *do_filp_open(int dfd, const char *pathname,
        int open_flag, int mode, int acc_mode)
{
    struct file *filp;
    struct nameidata nd;
    int error;
    struct path path;
    struct dentry *dir;
    int count = 0;
    int will_write;
    int flag = open_to_namei_flags(open_flag);
    ……………………

//初始化nameidata 类型实例nd.
    error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);

 

//处理查找操作,并在nameidata数据结构中存放指向目录项对象的指针和路径名最后一个分量对应的安装文件系统对象,nd中就存放了 文件路径名对应的索引节点的指针
    error = path_walk(pathname, &nd);

    …………
    filp = get_empty_filp();//获得一个空的file结构的空间
    if (filp == NULL)
        goto exit_parent;
    nd.intent.open.file = filp;//记录打开的文件
    nd.intent.open.flags = flag;
    nd.intent.open.create_mode = mode;
    dir = nd.path.dentry;
    nd.flags &= ~LOOKUP_PARENT;
    nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
    if (flag & O_EXCL)
        nd.flags |= LOOKUP_EXCL;
    mutex_lock(&dir->d_inode->i_mutex);
    path.dentry = lookup_hash(&nd);
    path.mnt = nd.path.mnt;


    /* Negative dentry, just create the file */
    if (!path.dentry->d_inode) {
        /*
         * This write is needed to ensure that a
         * ro->rw transition does not occur between
         * the time when the file is created and when
         * a permanent write count is taken through
         * the 'struct file' in nameidata_to_filp().
         */

        error = mnt_want_write(nd.path.mnt);
        if (error)
            goto exit_mutex_unlock;
        error = __open_namei_create(&nd, &path, flag, mode);
        if (error) {
            mnt_drop_write(nd.path.mnt);
            goto exit;
        }

//把辅助数据结构nd中存放的 path对应的inode对应的在内存中的文件地址,复制给filp
        filp = nameidata_to_filp(&nd, open_flag);
        if (IS_ERR(filp))
            ima_counts_put(&nd.path,
                 acc_mode & (MAY_READ | MAY_WRITE |
                         MAY_EXEC));
        mnt_drop_write(nd.path.mnt);
        if (nd.root.mnt)
            path_put(&nd.root);
        return filp;
    }

}


STEP 4:至此一个路径如/dev/dsp对应的file结构已经建立。file结构包含数据和方法,那么在对文件进行读写或操作的时候,调用的方法就是某个特定设备文件的方法。整个过程如下:
    进程---(fd)--->struct file---(f_dentry)--->struct dentry---(d_inode)---->struct inode---(i_sb)--->struct super_block---->找到了具体的文件系统了
阅读(1842) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~