Обратный путь в каталогах с помощью chdir() вместо абсолютных путей

В главе 4 книги "Расширенное программирование в среде Unix", в которой рассматриваются файлы и каталоги, приведен пример кода, который стремится быть похожим на ftw команда и пройти файловую иерархию. Он использует указатель на абсолютный путь к файлу, а также рекурсивную функцию с обратным вызовом для обхода каталога, используя вызовы opendir() а также readdir() в процессе.

Существует упражнение, в котором читателей просят использовать chdir() и имена файлов вместо использования абсолютных путей для выполнения одной и той же задачи и сравнения времени двух программ. Я написал программу, используя chdir() и не заметил разницы во времени. Это ожидается? Я бы подумал, что дополнительный вызов chdir() добавил бы некоторые накладные расходы. Может быть, это относительно тривиальный звонок? Любое понимание будет оценено.

Вот рекурсивная функция, использующая абсолютные пути:

static int                  /* we return whatever func() returns */
dopath(Myfunc* func)
{
    struct stat     statbuf;
    struct dirent   *dirp;
    DIR             *dp;
    int             ret;
    char            *ptr;

    if (lstat(fullpath, &statbuf) < 0) /* stat error */
        return(func(fullpath, &statbuf, FTW_NS));
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
        return(func(fullpath, &statbuf, FTW_F));

     /*
      * It's a directory. First call func() for the directory,
      * then process each filename in the directory.
      */
    if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
        return(ret);

    ptr = fullpath + strlen(fullpath);      /* point to end of fullpath */
    *ptr++ = '/';
    *ptr = 0;

     if ((dp = opendir(fullpath)) == NULL)     /* can't read directory */
         return(func(fullpath, &statbuf, FTW_DNR));

     while ((dirp = readdir(dp)) != NULL) {
         if (strcmp(dirp->d_name, ".") == 0 ||
             strcmp(dirp->d_name, "..") == 0)
                 continue;        /* ignore dot and dot-dot */

         strcpy(ptr, dirp->d_name);   /* append name after slash */

         if ((ret = dopath(func)) != 0)          /* recursive */
              break; /* time to leave */
     }
     ptr[-1] = 0;    /* erase everything from slash onwards */

     if (closedir(dp) < 0)
         err_ret("can't close directory %s", fullpath);

     return(ret);
}

А вот функция с моими изменениями:

static int                  /* we return whatever func() returns */
dopath(Myfunc* func, char* path)
{
    struct stat     statbuf;
    struct dirent   *dirp;
    DIR             *dp;
    int             ret;

    if (lstat(path, &statbuf) < 0) /* stat error */
        return(func(path, &statbuf, FTW_NS));
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
        return(func(path, &statbuf, FTW_F));

 /*
 * It's a directory. First call func() for the directory,
 * then process each filename in the directory.
 */
    if ((ret = func(path, &statbuf, FTW_D)) != 0)
        return(ret);

    if ( chdir(path) < 0 )
      return(func(path, &statbuf, FTW_DNR));

     if ((dp = opendir(".")) == NULL)     /* can't read directory */
         return(func(path, &statbuf, FTW_DNR));

     while ((dirp = readdir(dp)) != NULL) {
         if (strcmp(dirp->d_name, ".") == 0 ||
             strcmp(dirp->d_name, "..") == 0)
                 continue;        /* ignore dot and dot-dot */

         if ((ret = dopath(func, dirp->d_name)) != 0)          /* recursive */
              break; /* time to leave */
     }
     if ( chdir("..") < 0 )
       err_ret("can't go up directory");

     if (closedir(dp) < 0)
         err_ret("can't close directory %s", fullpath);

     return(ret);
}

1 ответ

Решение

Я не думаю, что вы должны ожидать существенную разницу во времени между версией абсолютного пути и chdir() версия. Скорее, плюсы и минусы обеих версий заключаются в следующем:

  • Полная версия пути не может проходить через очень глубокие структуры каталогов, поскольку длина полного пути в конечном итоге превышает PATH_MAX, chdir() Версия не имеет этой проблемы.
  • chdir() version манипулирует pwd, что обычно считается плохой практикой, если ее можно избежать: она не поточнобезопасна, и конечный пользователь может ожидать, что она останется одна. Например, имена файлов, заданные в командной строке и используемые другой частью программы, могут относиться к тому, что пользователь считал pwd, что ломается при его изменении.
  • chdir() версия может выйти из-под контроля при резервном копировании в более высокий каталог (chdir("..")), если особые меры предосторожности не приняты и структура каталогов изменяется во время прохождения. С другой стороны, полная версия пути в этих обстоятельствах может сломаться по-другому...

openat() Семейство функций, доступных в современных системах POSIX, предлагает лучшее из обоих миров. Если эти функции доступны, openat() вместе с fdopendir(), fstatat()и т.д... сделать для действительно хорошей реализации хождения по каталогам.

Другие вопросы по тегам