什么是telldir()函数的返回值?

程序:

#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()调用(对于任何目录)都会增加。
  • 对于包括Linux在内的其他主机 , 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_offdp->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_offdir_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[]) 。 代替。