2013-06-25 40 views
1

在覆盖文件和目录的书“UNIX环境高级编程”的第4章,有一个代码示例,旨在像ftw命令和遍历文件层次结构。它使用一个指向绝对文件路径的指针,以及一个带回调遍历目录的递归函数,在该过程中使用对opendir()readdir()的调用。目录遍历被chdir()而不是使用绝对路径

有一个练习,读者被要求使用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_MAXchdir()版本没有这个问题。
  • chdir()版本操纵pwd,如果可以避免,pwd通常被认为是不好的做法:它不是线程安全的,并且最终用户可能会认为它是独立的。例如,在命令行中给出的文件名以及程序的不同部分所使用的文件名可能与用户认为pwd是什么有关,当您更改它时会中断它。
  • 备份到较高目录(chdir(".."))时,chdir()版本可能会失去控制,如果不采取特别措施并且在遍历目录结构时发生更改。然后在这种情况下,完整的路径名版本可能会以不同的方式破坏...

现代POSIX系统上可用的openat()系列功能提供了两全其美的功能。如果这些功能可用,openat()连同fdopendir(),fstatat()等等,使得目录散步非常好。

+0

感谢您的洞察力和其他功能! – rsa