2015-09-15 5 views
2

Программы:Каково возвращаемое значение функции 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: d.c    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() является «вернуть текущее местоположение в потоке каталогов ". Итак, я ожидаю, что каталог содержит 5 файлов, поэтому при первом вызове telldir() возвращает 1 и в следующем вызове return 2. Но здесь результат умножен на 32. Как выглядит вывод? И почему функция telldir() возвращает этот вид значения?

+5

Возможно, вы захотите прочитать раздел ** Примечания ** на [этой странице руководства] (http://man7.org/linux/man-pages/man3/telldir.3.html). –

+0

похоже на страницу руководства. Никакой дополнительной информации здесь не упоминается. – mrg

+11

Из [ссылки на страницу руководства] (http://man7.org/linux/man-pages/man3/telldir.3.html): «Современные файловые системы используют структуры дерева или хеша, а не плоские таблицы, для представления каталогов. В таких файловых системах значение, возвращаемое telldir() (и используемое внутренне с помощью readdir (3)), является «cookie», которое используется реализацией для получения позиции в каталоге. *** Прикладные программы должны обрабатывать это строго как непрозрачное значение, не делая никаких предположений о его содержании *** ». Функция «telldir» возвращает значение, какое значение имеет значение, не интерпретирует его определенным образом. –

ответ

2

Единственное POSIX states о «месте», возвращенного telldir() является: функция

seekdir() устанавливает позицию следующей операции READDIR() на потоке каталога, указанного DIRP в положение указанный loc. Значение loc должно быть возвращено из предыдущего вызова telldir(). Новое положение возвращается к тому, которое было связано с потоком каталога, когда была выполнена telldir().

Если значение loc не было получено из более раннего вызова telldir() или если был вызван вызов rewinddir() между вызовом telldir() и вызовом seekdir(), результаты последующих вызовов todddir() не определены.

Таким образом, возвращаемые значения telldir() определены реализацией. Это не номер текущего файла.


Если вы посмотрите на реализации GLibC, вы увидите, что:

  • для some hosts, telldir() возвращает печенье на основе глобального счетчика, который увеличивается на единицу каждый telldir() вызова (для любого каталога);
  • для other hosts, включая Linux, telldir() возвращает «смещение файла», которое is later passed - lseek().

Даже если telldir() возвращает «смещение файла», это смещение может иметь особое значение для каталога, в зависимости от реализации файловой системы.

Например, для ext4 см ext4_dir_llseek():

ext4_dir_llseek() вызывает generic_file_llseek_size обрабатывать HTree каталоги, где «смещение» в терминах имени файла хэш-значения вместо смещения байта.


В моей системе, например, выход из вашей программы (для ext4):

Filename: ..   Location in Directory Stream: 3540738803800888240 
Filename: foo  Location in Directory Stream: 5674377099084065539 
Filename: .   Location in Directory Stream: 9223372036854775807 
0

telldir() функция возвращает позицию в каталоге, код может быть как:

long int 
    telldir (DIR *dirp) 
    { 
     return dirp->filepos; 
    } 

Когда вы вызываете opendir(), файл находится 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 дп - предыдущая and call __put_user (ctx-> пос, & DP-> d_off) , then the DP-> d_off is set (as you see, the ctx-> пос is the offset of previous entry's end), and telldir() will return the position of the last entry you got via readdir() `.

NB void main() не является хорошим способом, вы можете использовать int main(int argc, char *argv[]). вместо.