程序:
#include<stdio.h> #include<dirent.h> void main() { int i=0; DIR *dir=opendir("dir"); struct dirent *dent; while((dent=readdir(dir))!=NULL){ printf("Filename: %s\t\t Location in Directory Stream: %ld\n", dent->d_name,telldir(dir)); } }
输出:
$ ./a.out Filename: b.txt Location in Directory Stream: 32 Filename: a.txt Location in Directory Stream: 64 Filename: dc Location in Directory Stream: 96 Filename: . Location in Directory Stream: 128 Filename: test Location in Directory Stream: 160 Filename: .. Location in Directory Stream: 192 $
在上面的程序中, telldir()
函数的返回值是32的倍数。对telldir()
手册页的引用是“返回目录stream中的当前位置”。 所以,我期望目录包含5个文件,所以首先调用telldir()
返回1,在下一次调用它返回2.但这里的输出是32的倍数。为什么这样的输出? 为什么telldir()
函数返回这种值?
POSIX唯一的关于由telldir()
返回的“location”是:
seekdir()函数应将由dirp指定的目录流上的下一个readdir()操作的位置设置为由loc指定的位置。 loc的值应该早先从telldir()调用中返回。 执行telldir()时,新位置将恢复为与目录流关联的位置。
如果loc的值不是从先前调用telldir()获得的,或者在调用telldir()和调用seekdir()之间发生对rewinddir()的调用,则对readdir()没有说明。
因此, telldir()
返回值是实现定义的。 这不是当前文件的数量。
如果你看glibc实现,你会看到:
telldir()
返回一个基于全局计数器的cookie,每个telldir()
调用(对于任何目录)都会增加。 telldir()
返回一个稍后传递给lseek()
的“文件偏移量”。 即使telldir()
返回“文件偏移量”,这个偏移量对于一个目录可能有特殊的意义,这取决于文件系统的实现。
例如,对于ext4
请参阅ext4_dir_llseek()
:
ext4_dir_llseek()调用generic_file_llseek_size来处理htree目录,其中“offset”是根据文件名散列值而不是字节偏移量。
在我的系统中,程序的输出示例是(对于ext4
):
Filename: .. Location in Directory Stream: 3540738803800888240 Filename: foo Location in Directory Stream: 5674377099084065539 Filename: . Location in Directory Stream: 9223372036854775807
telldir()
函数返回目录中的位置,代码可能如下所示:
long int telldir (DIR *dirp) { return dirp->filepos; }
当你调用opendir()
,filepos是0,那么readdir()
将调用gentdents()
,并添加dirp->filepos
。 例如,在调用readdir()
之后, dirp->filepos = dp->d_off
, dp->d_off
将由file->f_op->iterate()
来设置。 对于文件系统PFS(我自己设计的linux文件系统驱动程序), iterate()
是pfs_readdir()
:
static int pfs_readdir(struct file *file, struct dir_context *ctx) { int64_t dno; unsigned long off; struct buffer_head *bh; struct pfs_dir_entry *de; struct inode *inode = file_inode(file); if(ctx->pos == 0) ctx->pos = PFS_DIRHASHSIZ * sizeof(int64_t) + sizeof(int64_t); for(off = ctx->pos & (PFS_BLOCKSIZ - 1); ctx->pos < inode->i_size; off = ctx->pos & (PFS_BLOCKSIZ - 1)){ if(!(dno = pfs_get_block_number(inode, pfs_block_number(ctx->pos), 0))) goto skip; if(!(bh = sb_bread(inode->i_sb, dno / PFS_STRS_PER_BLOCK))){ pr_err("pfs: device %s: %s: failed to read block %lld of dir %lld\n", inode->i_sb->s_id, "pfs_readdir", pfs_block_number(ctx->pos), PFS_I(inode)->i_ino); goto skip; } do{ de = (struct pfs_dir_entry *)((char *)bh->b_data + off); if(de->d_ino){ if(!(dir_emit(ctx, pfs_get_de_name(de), de->d_len, (int32_t)le64_to_cpu(de->d_ino), DT_UNKNOWN))){ brelse(bh); return 0; } } off += pfs_get_de_size(de); ctx->pos += pfs_get_de_size(de); }while(off < PFS_BLOCKSIZ && ctx->pos < inode->i_size); brelse(bh); continue; skip: ctx->pos += PFS_BLOCKSIZ - off; } return 0; }
dp->d_off
由dir_emit()
设置,源为:
static inline bool dir_emit(struct dir_context *ctx, const char *name, int namelen, u64 ino, unsigned type) { return ctx->actor(ctx, name, namelen, ctx->pos, ino, type) == 0; }
正如你看到的, ctx->actor
将调用filldir()
, filldir() will set
dp – previous and call
__put_user(ctx-> pos,&dp-> d_off) , then the
dp-> d_off is set (as you see, the
ctx-> pos is the offset of previous entry's end), and
telldir() will return the position of the last entry you got via
readdir()` will return the position of the last entry you got via
。
注意void main()
不是一个好办法,可以用int main(int argc, char *argv[])
。 代替。