5 Open操作和Close操作

5.1   Open操作
系统调用open是由函数sys_open(fs/open.c)实现的。该系统调用用来获得欲访问文件的文件描述符。如果文件不存在则可以用它创建一个新文件。打开的文件不存在时,使用三个参数形式:filename,falges,mode。

⑴参数filename是指向所要打开文件的路径名指针。

⑵参数flags指定打开文件的方式,它必须包含下列三个值之一:O_RDONLY(只读打开);O_WRONLY(只写打开);O_RDWR(读/写打开)

⑶参数mode指定对文件所有者、文件用户组及系统中所有其他用户的访问权限位。

具体实现如下:

asmlinkage long sys_open(const char * filename, int flags, int mode)

{

     char * tmp;

     int fd, error;

#if BITS_PER_LONG != 32

     flags |= O_LARGEFILE;#endif                     

tmp = getname(filename);   

/* 调用getname()函数作路径名的合法性检查。getname()在检查名字错误的同时要

把filename从用户区拷贝到核心资料区。它会检查给出的地址是否在当前进程的虚

存段内,在核心空间中申请一页,并不断把filename字符的内容拷贝到该页中去。

这样是为了使系统效率提高,减少不比要的操作。*/

     fd = PTR_ERR(tmp);

     if (!IS_ERR(tmp)) {

        fd = get_unused_fd();

/* 调用get_unused_fd(),获取一个文件描述符。*/

        if (fd >= 0) {

struct file *f = filp_open(tmp, flags, mode);     

/* 调用filp_open(),获取文件对应的filp结构。*/

            error = PTR_ERR(f);

            if (IS_ERR(f))

goto out_error;     

/* 如果所获file结构有错误,跳到out_error处释放文件描述符,返回出错。*/

fd_install(fd, f);        

/* 调用fd_install()将打开文件的file结构装入当前进程打开文件表中。*/

        }

out:

        putname(tmp);

     }

     return fd;

out_error:

     put_unused_fd(fd);

     fd = error;

     goto out;

}

int get_unused_fd(void)

{

     struct files_struct * files = current->files;  

/* 获取当前进程打开文件表。*/

     int fd, error;

     error = -EMFILE;

     write_lock(&files->file_lock);

repeat:

fd = find_next_zero_bit(files->open_fds,      

/* files->open_fds是打开文件描述符的位图,files->open_fds为下一个可以打开

文件的描述符,files->max_fdset为可以打开文件的最大数目。通过这些参数,我们

调用find_next_zero_bit()来获取一个空闲的文件描述符。*/

               files->max_fdset,

               files->next_fd);

     /*

      * N.B. For clone tasks sharing a files structure, this test

      * will limit the total number of files that can be opened.

      */

if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)       

/* 如果fd大于进程可用最大文件数目直接退出。*/

        goto out;

     /* Do we need to expand the fdset array? */

if (fd >= files->max_fdset) {     

/* 如果fd大于files->max_fdset,那调用expand_fdset()扩展files->open_fds

与files->close_on_exec,用于标记更多的文件。如果扩展成功,那要跳回上面,重

新调用find_next_zero_bit()来获取一个文件描述符。如果出错则退出。*/

        error = expand_fdset(files, fd);

        if (!error) {

            error = -EMFILE;

            goto repeat;

        }

        goto out;

     }

    

     /*

      * Check whether we need to expand the fd array.

      */

if (fd >= files->max_fds) {      

/* 如果fd大于files->max_fds,那调用expand_fd_array()扩展file结构数组fd

(这个fd是指files_struct中的struct file * * fd,与该程序中的变量fd 不同)。

如果扩展成功,那要跳回到上面重新调用find_next_zero_bit()来获取一个文件描述

符。如果出错则退出。*/

        error = expand_fd_array(files, fd);

        if (!error) {

            error = -EMFILE;

            goto repeat;

        }

        goto out;

     }

     FD_SET(fd, files->open_fds);          

/* 将files->open_fds中相应的fd位置位。*/

     FD_CLR(fd, files->close_on_exec);      

/* 清除files->close_on_exec中的相应位。*/

     files->next_fd = fd + 1;

#if 1

     /* Sanity check */

     if (files->fd[fd] != NULL) {      

/*如果files->fd[fd]不为空,那打印出提示信息并将其置空。*/

        printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);

        files->fd[fd] = NULL;

     }

#endif

     error = fd;

out:

     write_unlock(&files->file_lock);

     return error;

}

struct file *filp_open(const char * filename, int flags, int mode)

{

     int namei_flags, error;

     struct nameidata nd;

     namei_flags = flags;

     if ((namei_flags+1) & O_ACCMODE)

        namei_flags++;

     if (namei_flags & O_TRUNC)

        namei_flags |= 2;

     error = open_namei(filename, namei_flags, mode, &nd);

     if (!error)

        return dentry_open(nd.dentry, nd.mnt, flags);  

/* filp_open()的工作大体分成两部分1.调用open_namei(),通过路径名获取其相

应的dentry与vfsmount结构。 2.调用dentry_open(),通过open_namei得到dentry

与vfsmount来得到file结构。(下面马上介绍。)*/

     return ERR_PTR(error);

}

调用dentry_open(),通过open_namei得到dentry与vfsmount来得到file结构。

struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)

{

     struct file * f;

     struct inode *inode;

     static LIST_HEAD(kill_list);

     int error;

     error = -ENFILE;

f = get_empty_filp();    

/* 调用get_empty_filp(),创建一个file结构,并将这个结构链入anon_list链表。*/

     if (!f)

        goto cleanup_dentry;      

/* 如果创建file结构失败,那释放dentry与vfsmount,返回出错。*/

     f->f_flags = flags;

     f->f_mode = (flags+1) & O_ACCMODE;

/*对file结构中的f_flags、f_mode赋值。*/

     inode = dentry->d_inode;

     if (f->f_mode & FMODE_WRITE) {

/* 检查对打开文件是否以写模式打开。若是则调用get_write_access()获取对文件的写权限。get_write_access()注意通过对inode 的i_writecount项的操作来实现其功能。*/

        error = get_write_access(inode);

        if (error)

            goto cleanup_file;

     }

     f->f_dentry = dentry;

/ *对file结构中的相关项赋值。*/

     f->f_vfsmnt = mnt;

     f->f_pos = 0;

     f->f_reada = 0;

     f->f_op = fops_get(inode->i_fop);

     file_move(f, &inode->i_sb->s_files);

/* 将file结构挂到超级块打开文件链表上。*/

     /* preallocate kiobuf for O_DIRECT */

     f->f_iobuf = NULL;

     f->f_iobuf_lock = 0;

     if (f->f_flags & O_DIRECT) {

        error = alloc_kiovec(1, &f->f_iobuf);

        if (error)

            goto cleanup_all;

     }

     if (f->f_op && f->f_op->open) {

/* 将file结构挂到超级块打开文件链表上。*/

        error = f->f_op->open(inode,f);

        if (error)

            goto cleanup_all;

     }

     f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

/* 改变file结构的标志项。*/

     return f;

cleanup_all:

     if (f->f_iobuf)

        free_kiovec(1, &f->f_iobuf);

     fops_put(f->f_op);

     if (f->f_mode & FMODE_WRITE)

        put_write_access(inode);

     file_move(f, &kill_list); /* out of the way.. */

     f->f_dentry = NULL;

     f->f_vfsmnt = NULL;

cleanup_file:

     put_filp(f);

cleanup_dentry:

     dput(dentry);

     mntput(mnt);

     return ERR_PTR(error);

}

open操作用到的主要函数的调用关系结构如下图所示

sys_open

get_unused_fd

file_open

fd_install

open_namei

dentry_open

path_walk

real_lookup

图6

5.2   Close操作
关闭文件时要调用sys_close()来实现。关闭打开的文件描述符, int close (unsigned int   fd),函数调用成功返回0值,否则返回-1值。由errno存放错误编号EBAD表示参数是一个有效的打开的文件描述符。

系统调用open()打开一个文件时系统就分配给文件一个文件描述符,同时为打开文件描述符的引用计数加1。调用close()关闭文件时打开文件描述符的引用计数减1。引用计数为0时才彻底关闭打开的文件。

具体实现如下:

asmlinkage long sys_close(unsigned int fd)

{

    struct file * filp;

    struct files_struct *files = current->files;

/* 将局部变量files指向当前进程的打开文件表。*/

    write_lock(&files->file_lock);

/* 对该表设置读写自旋锁。*/

    if (fd >= files->max_fds)

/* 如果函数传入参数fd(打开文件描述符)大于该表的最大打开文件数目则返回出错。*/

        goto out_unlock;

     filp = files->fd[fd];

/* 根据文件描述符,从打开文件表中得到相应文件的file结构。*/

    if (!filp)

/* 如果对应的file结构指针为空,那返回出错。*/

        goto out_unlock;

    files->fd[fd] = NULL;

/* 将打开文件表中文件描述符所对应的指针置为空。*/

    FD_CLR(fd, files->close_on_exec);

/* 将close_on_exec中的fd位置为0。*/

    __put_unused_fd(files, fd);

/* 将open_fds项的fd位置为0。并且比较一下fd与next_fd,那将fd的值赋给next_fd,作为未使用的文件描述符提供给后面打开的文件。*/

    write_unlock(&files->file_lock);

/* 解开读写自旋锁。*/

    return filp_close(filp, files);

/* 调用file_close()做余下的工作。*/

out_unlock:

    write_unlock(&files->file_lock);

    return -EBADF;

}

int filp_close(struct file *filp, fl_owner_t id)

{

    int retval;

    if (!file_count(filp)) {

/* 读取文件引用数。若为0直接返回。*/

        printk(KERN_ERR "VFS: Close: file count is 0\n");

        return 0;

    }

    retval = 0;

    if (filp->f_op && filp->f_op->flush) {

        lock_kernel();

        retval = filp->f_op->flush(filp);

        unlock_kernel();

    }

/* 如果文件系统有自己的操作函数flush,那直接调用该函数。在这个操作时要设置内核锁。*/

    fcntl_dirnotify(0, filp, 0);

    locks_remove_posix(filp, id);

    fput(filp);

/* 调用fput()释放文件对应的file结构。*/

    return retval;

}

void fput(struct file * file)

{

    struct dentry * dentry = file->f_dentry;

    struct vfsmount * mnt = file->f_vfsmnt;

    struct inode * inode = dentry->d_inode;

    if (atomic_dec_and_test(&file->f_count)) {

/* 对文件引用数减1,并判断&file->f_count减1后是否为0。若为0则做后面的工作,否则结束该函数。*/

        locks_remove_flock(file);

/* 除去文件锁。*/

        if (file->f_op && file->f_op->release)

           file->f_op->release(inode, file);

/* 如果文件系统有自己的release函数,那直接调用该函数来释放该文件。*/

        fops_put(file->f_op);

/* 减少文件操作函数模块的引用次数。*/

        file->f_dentry = NULL;

        file->f_vfsmnt = NULL;

/* 将file->f_dentry、file->f_vfsmnt置为NULL, 但此时并没有释放相对应的dentry与vfsmount结构,局部变量dentry和mnt还指向这两个结构。*/

if (file->f_mode & FMODE_WRITE)

           put_write_access(inode);

/* 如果f_mode的FMODE_WRITE位被置位了,那么调用put_write_access,将文件对应的inode的i_writecount项减1。*/

    dput(dentry);

/* 调用dput释放dentry。*/

        if(mnt);

/* 如果mnt存在,调用mntput释放vfsmount。*/

mntput(mnt);

file_list_leck();

        list_del(&file->f_list);

        list_add(&file->f_list, &free_list);

        files_stat.nr_free_files++;

/* 将该节点从超级块中的打开文件链表中删除,加到free_list链表上,并对files_stat.nr_free_files做加1操作。*/

        file_list_unlock();

    }

}

void dput(struct dentry *dentry)

{

    if (!dentry)

/* 如果dentry为空,那立刻返回。*/

        return;

repeat:

    if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))

/* 这里只对dentry->d_count做减1操作,如果减1后dentry->d_count不为0,那立刻返回。*/

        return;

    /* dput on a free dentry? */

    if (!list_empty(&dentry->d_lru))

/* 如果该dentry在d_count= =0的队列(即dentry_unused)上,那么调用BUG()

        BUG(); */

    /*

     * AV: ->d_delete() is _NOT_ allowed to block now.

     */

    if (dentry->d_op && dentry->d_op->d_delete) {

/* 如果该dentry有自己的d_delete()函数,那直接调用它,指向完之后,调到unhash_it。*/

        if (dentry->d_op->d_delete(dentry))

           goto unhash_it;

    }

    /* Unreachable? Get rid of it */

    if (list_empty(&dentry->d_hash))

/* 如果该dentry不在hash表上,那跳到kill_it。*/

        goto kill_it;

    list_add(&dentry->d_lru, &dentry_unused);

/* 将dentry加到dentry_unused队列上。*/

    dentry_stat.nr_unused++;

/*

*Update the timestamp

*/

dentry->d_reftime = jiffies;

/* 更新dentry的时间戳,记录它最后一次引用的时间。*/

    spin_unlock(&dcache_lock);

    return;

unhash_it:

    list_del_init(&dentry->d_hash);

/* 将dentry从hash表中删除。*/

         

kill_it: {

        struct dentry *parent;

        list_del(&dentry->d_child);

/* 将该dentry从其同一级目录链表中删除。*/

        /* drops the lock, at that point nobody can reach this dentry */

        dentry_iput(dentry);

/* 用于删除dentry对应的inode。*/

        parent = dentry->d_parent;

        d_free(dentry);

/* 释放dentry的内存空间。*/

        if (dentry == parent)

           return;

        dentry = parent;

/* 将变量dentry指向目前目录的父目录的dentry结构,跳到repeat。这样是为了将父目录dentry结构的引用次数减1,直到根目录为止。*/

        goto repeat;

    }

}

static inline void dentry_iput(struct dentry * dentry)

{

    struct inode *inode = dentry->d_inode;

/* 判断一下dentry中的inode是否为空。*/

    if (inode) {

        dentry->d_inode = NULL;

/* 将dentry->d_inode指向空。*/

        list_del_init(&dentry->d_alias);

/* 将该dentry结构从inode的i_dentry链表中删去。i_dentry链表将通过硬链接指向同一个inode的dentry结构。*/

        spin_unlock(&dcache_lock);

        if (dentry->d_op && dentry->d_op->d_iput)

/* 如果该dentry中带有自己的d_iput的函数,则调用它。否则调用iput函数。*/

           dentry->d_op->d_iput(dentry, inode);

        else

           iput(inode);

    } else

        spin_unlock(&dcache_lock);

}

close操作用到的主要函数的调用关系结构如下图所示:

sys_close

_put_unused_fd

file_close

fput

dput

dentry_iput

mvtput

iput

图7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值