Linux论坛's Archiver

《开源》旗舰电子杂志2009年第1期火热下载!

hyl 发表于 2008-4-24 15:51

重读2.4 050 fs/readdir.c

[url]http://docs.google.com/Doc?id=dcbsxfpf_211fx8xtr7j[/url]


2008-4-22



fs/readdir.c



此文件很纯净,就是你ls的时候所调用的函数, 在 UML
一文中已经讨论过这个文件,这里忽略64bit的处理,仅简单看看.



int vfs_readdir(struct file *file, filldir_t filler, void *buf)

{

        .....

        if (!IS_DEADDIR(inode)) {

                lock_kernel();

                res = file->f_op->readdir(file, buf, filler); /*就是调用fop的readdir*/

                unlock_kernel();

        }

        .....

        return res;

}



对于不同文件系统的readdir实现不同, 其中对于像shm
fs,ramfs这种纯粹虚拟的文件系统,这里提供了一个统一的实现:

int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)

{

        struct dentry *dentry = filp->f_dentry;

        struct dentry *cursor = filp->private_data; /*dcache_dir_open 将cursor
设置为dentry ''.''*/

                                         /* cursor d_child
挂入了filp->f_dentry->d_subdirs */

        struct list_head *p, *q = &cursor->d_child;

        ino_t ino;

        int i = filp->f_pos;



        switch (i) {

                 .... /*. 和.. 处理,略*/

                default:

                        spin_lock(&dcache_lock);

                        if (filp->f_pos == 2) { /* pos=2 意味从头开始 */

                                list_del(q);/*将cursor从f_dentry->d_subdirs 删除*/

                                list_add(q, &dentry->d_subdirs);/*又加回来,从而位于subdir之首*/

                        }



                        /*从cursor 的下一个subdir 开始*/

                        for (p=q->next; p != &dentry->d_subdirs; p=p->next) {

                                struct dentry *next;

                                next = list_entry(p, struct dentry, d_child);

                                if (d_unhashed(next) || !next->d_inode)
/*只有加入hash的dentry并且有inode(未删除)*/

                                        continue;         /*删除dentry可能还在hash中*/



                                spin_unlock(&dcache_lock);

                                if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos,

                                next->d_inode->i_ino, dt_type(next->d_inode)) < 0)

                                        return 0;

                                spin_lock(&dcache_lock);

                                /* next is still alive */

                                list_del(q); /*q 是cursor, 后移一个位置 */

                                list_add(q, p);

                                p = q; /*从cursor 取下一个subdir*/

                                filp->f_pos++;

                        }

                        spin_unlock(&dcache_lock);

        }

        return 0;

}



至于ext2的readdir操作就不多说了,不是很难.

static int ext2_readdir(struct file * filp,

                         void * dirent, filldir_t filldir)

{

        offset = filp->f_pos & (sb->s_blocksize - 1); /*计算文件pos在blk中的偏移*/



        while (!error && !stored && filp->f_pos < inode->i_size) {

                /*stored:如果已读取当前blk内的dentry,就不读下个blk的entry了..
咋想的呢?*/

                blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); /*计算blk no*/

                bh = ext2_bread (inode, blk, 0, &err); /*文件内blk到文件系统blk
no的换算:ext自己的三级表...*/

                if (!bh) { /*有个洞? ...有点问题,打印错误信息,继续*/

                             .....

                        filp->f_pos += sb->s_blocksize - offset;

                        continue;

                }

                /* Do the readahead
:找找文件块到文件系统块的转换表,然后预读,不像ext2_bread是block的*/

                .....

               

revalidate:

                /*这个修改的侦测只对f_pos所在blk有效,有限的避免竞争情况下的不一致*/

                if (filp->f_version != inode->i_version) {
/*f_version用于监视文件是否被修改过*/

                    
/*不一样的情况下记录的f_ops可能是有问题的,再次从0到offset重新开始扫描下,停到有问题的块
上..*/

                        for (i = 0; i < sb->s_blocksize && i < offset; ) {

                                de = (struct ext2_dir_entry_2 *)

                                        (bh->b_data + i);

                                停到第一个有问题的entry上,

                        }

                        ....

                        filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
/*更新文件的f_ops*/

                                | offset;

                        filp->f_version = inode->i_version;   

                }

               

                while (!error && filp->f_pos < inode->i_size

                       && offset < sb->s_blocksize) { /*尝试读取此blk内所有目录项*/

                        de = (struct ext2_dir_entry_2 *) (bh->b_data + offset);

                        if (!ext2_check_dir_entry ("ext2_readdir", inode, de,

                                                   bh, offset)) {

                                /*如果有问题,停止读取(ext2删除dentry时把前一个dentry的长度扩大,覆盖被删除的dentry*
/

                                .... /*跟新f_pos,返回*/

                                return stored;

                        }

                        offset += le16_to_cpu(de->rec_len);

                        if (le32_to_cpu(de->inode)) {

                                /* copy 出去一个 */

                                /* 缓冲区满就返回了*/

                                if (version != filp->f_version)
/*竞争发生时重新validate*/

                                        goto revalidate;

                                stored ++;

                        }

                        filp->f_pos += le16_to_cpu(de->rec_len);

                }

                offset = 0;

                brelse (bh);

        } //end while

        UPDATE_ATIME(inode);

        return 0;

}

页: [1]

Powered by Discuz! Archiver 7.0.0  © 2001-2009 Comsenz Inc.