天天看點

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;  

/* 擷取目前程序打開檔案表。*/

     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;

      * 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);

     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;

     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);

            goto cleanup_all;

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

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

     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);

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結構指針為空,那傳回出錯。*/

    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:

    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;

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

/* 這裡隻對dentry->d_count做減1操作,如果減1後dentry->d_count不為0,那立刻傳回。*/

    /* 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

close操作用到的主要函數的調用關系結構如下圖所示:

sys_close

_put_unused_fd

file_close

fput

dput

dentry_iput

mvtput

iput

圖7

<a href="http://hi.baidu.com/tag/%E5%9C%A8csdn%E6%91%98%E5%BD%95%E7%9A%84unix%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%A8%A1%E6%8B%9F%E7%9A%84%E5%AE%9E%E7%8E%B0/feeds">#在csdn摘錄的unix檔案系統模拟的實作</a>