从函数readdir

时间:2015-12-31 19:18:24

标签: c linux stack malloc free

我在Linux环境中使用C编程语言来读取目录中的文件。我在代码中添加了#include<dirent.h>,并使用了函数readdir()

根据在线的Linux页面,它说不会在free()结构的结果指针上调用dirent,因为它可能被分配在堆栈上。

你能帮我理解它是如何工作的吗?我不明白为什么我们不必删除struct dirent。什么时候删除它谁删除它?

Here是我所说的摘录:

  

成功时,readdir()返回指向dirent结构的指针。 (此结构可以静态分配;不要尝试free(3)它。)如果到达目录流的末尾,则返回NULL并且不更改errno。如果发生错误,则返回NULL并正确设置errno

5 个答案:

答案 0 :(得分:10)

man readdir字面上说:

  

成功时,readdir()返回指向dirent结构的指针。   (这种结构可以静态分配;不要尝试   free(3)它。)

(已添加代码格式化程序。)

这意味着它的空间不是在运行时分配的,例如堆栈或空闲存储内存,而是static:它在可执行文件本身,与字符串文字相比,区别在于写入字符串文字是未定义的行为。

想象一下这样的实现:

struct dirent *readdir(DIR *dirp) {
    static struct dirent dir;

    /* Fill dir with appropriate values. */

    return &dir;
}

dir在这里静态分配。返回它的地址是错误的,因为它存在于程序的整个运行时间内。

以下是我的glibc 2.22实现中readdir的实际源代码(路径为/sysdeps/posix/readdir.c):

DIRENT_TYPE *
__READDIR (DIR *dirp)
{
  DIRENT_TYPE *dp;
  int saved_errno = errno;

#if IS_IN (libc)
  __libc_lock_lock (dirp->lock);
#endif

  do
    {
      size_t reclen;

      if (dirp->offset >= dirp->size)
    {
      /* We've emptied out our buffer.  Refill it.  */

      size_t maxread;
      ssize_t bytes;

#ifndef _DIRENT_HAVE_D_RECLEN
      /* Fixed-size struct; must read one at a time (see below).  */
      maxread = sizeof *dp;
#else
      maxread = dirp->allocation;
#endif

      bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
      if (bytes <= 0)
        {
          /* On some systems getdents fails with ENOENT when the
         open directory has been rmdir'd already.  POSIX.1
         requires that we treat this condition like normal EOF.  */
          if (bytes < 0 && errno == ENOENT)
        bytes = 0;

          /* Don't modifiy errno when reaching EOF.  */
          if (bytes == 0)
        __set_errno (saved_errno);
          dp = NULL;
          break;
        }
      dirp->size = (size_t) bytes;

      /* Reset the offset into the buffer.  */
      dirp->offset = 0;
    }

      dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];

#ifdef _DIRENT_HAVE_D_RECLEN
      reclen = dp->d_reclen;
#else
      /* The only version of `struct dirent*' that lacks `d_reclen'
     is fixed-size.  */
      assert (sizeof dp->d_name > 1);
      reclen = sizeof *dp;
      /* The name is not terminated if it is the largest possible size.
     Clobber the following byte to ensure proper null termination.  We
     read jst one entry at a time above so we know that byte will not
     be used later.  */
      dp->d_name[sizeof dp->d_name] = '\0';
#endif

      dirp->offset += reclen;

#ifdef _DIRENT_HAVE_D_OFF
      dirp->filepos = dp->d_off;
#else
      dirp->filepos += reclen;
#endif

      /* Skip deleted files.  */
    } while (dp->d_ino == 0);

#if IS_IN (libc)
  __libc_lock_unlock (dirp->lock);
#endif

  return dp;
}

我不太了解glibc,但行

dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];

对我们来说似乎最有趣。据我所知,此处dirp->datastatic数据。

这就是为什么有可重入替代readdir_rreaddir 不可重入的原因。 想象两个线程同时执行readdir。两者都会尝试同时填充所有dir个调用之间共享的readdir,从而导致无序的内存读/写。

答案 1 :(得分:3)

您引用的 man page 警告struct dirent statically allocated 。因此free()不是必需的。

free() 专门用于 [m][c][re]alloc() 函数,这些函数都来自 {{3}的内存请求} 的。 (而不是堆栈)

答案 2 :(得分:1)

您只需要false先前通过拨打free()和家人分配的内存malloc()。这是dynamically allocated memory,来自 heap 区域。

OTOH,非动态分配(请参阅automatic allocation)无需单独处理。一旦变量超出范围,就会回收堆栈内存并根据需要重新使用。

答案 3 :(得分:1)

您不需要释放/释放由readdir()提取的条目。它使用静态内部缓冲区,因此不需要动态释放,因为它没有动态分配。请注意,编译器可以预测所需的空间,因为您一次只使用一个条目,而您只需要一个条目来存储结果。这就是为什么它也不可重入的原因。有一个readdir_r(),它接受​​用户分配的缓冲区,当然是可重入的。

您需要在closedir()指针上调用DIR *,以释放opendir()使用的资源。

答案 4 :(得分:0)

struct dirent逻辑上是DIR的一部分。它可能会在同一个readdir()的后续DIR来电中重复使用(但不会在readdir()上的DIR来电中重复使用),并且会在closedir()时被释放}。

某些文档指出readdir()不是线程安全的。在除了非常奇特的实现之外的所有实现中,只要在下一次struct dirent调用之前对上一个readdir()的最后一次访问发生,它就是线程安全的。使用readdir_r()是不可取的,因为很难正确确定NAME_MAX限制,readdir_r()函数不知道其调用者使用的值。

有关readdir_r()问题的详细信息,请访问http://austingroupbugs.net/view.php?id=696