重读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]