Pregunta Libere (borre) la memoria asignada de la función readdir


Estoy usando el lenguaje de programación C en el entorno Linux para leer los archivos en un directorio. He incluido #include<dirent.h> en mi código y estoy usando la función readdir().

Según la página de Linux en línea, dice no llamar free() en el puntero resultante a una dirent estructura porque puede asignarse en la pila.

¿Puedes ayudarme a entender cómo funciona eso? No entiendo por qué no tendríamos que borrar el struct dirent. ¿Cuándo se elimina y quién lo elimina?

aquí es el extracto del que estoy hablando:

En el éxito, readdir() devuelve un puntero a un dirent estructura. (Esta estructura puede asignarse estáticamente, no intente free(3) it.) Si se llega al final de la secuencia de directorio, NULL es devuelto y errno no ha cambiado Si ocurre un error, NULL es devuelto y errno está configurado apropiadamente.


5
2017-12-31 19:18


origen


Respuestas:


man readdir literalmente dice:

En el éxito, readdir() devuelve un puntero a una estructura dirent.   (Esta estructura puede asignarse estáticamente, no intente    free(3) eso.)

(Se agregaron formateadores de código).

Eso significa que el espacio para ello no está asignado en el tiempo de ejecución, como la pila o la memoria de la tienda libre, pero es static: está en el ejecutable mismo, comparable a los literales de cadena con la diferencia de que escribir en literales de cadena es un comportamiento indefinido.

Imagina que la implementación sea algo como esto:

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

    /* Fill dir with appropriate values. */

    return &dir;
}

dir está estáticamente asignado aquí. Devolver su dirección no es incorrecto porque existe durante todo el tiempo de ejecución del programa.

Aquí está el código fuente real de readdir en mi implementación de glibc 2.22 (la ruta es /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;
}

No sé mucho sobre glibc pero la línea

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

Parece lo más interesante para nosotros. dirp->data es el static datos aquí, por lo que puedo decir.


Esa es la razón por la cual existe la alternativa reentrante. readdir_r y readdir es no reentrada.
Imagina dos hilos ejecutando a la vez. readdir. Ambos intentarán llenar dir, que se comparte entre todos readdir invocaciones, simultáneamente, lo que resulta en lecturas / escrituras de memoria no secuenciadas.


10
2017-12-31 19:21



los página man Tu referencia es advertir que la struct dirent es asignados estáticamente. Por lo tanto free()no es necesario.

gratis() está diseñado exclusivamente para su uso con [m] [c] [re] alloc () funciones, que todas hacen solicitudes de memoria desde el montón. (a diferencia de la pila)


3
2017-12-31 19:23



Solo necesitas free() la memoria que ha sido previamente asignada por una llamada a malloc() y familia. Esto es memoria asignada dinámicamente, de montón zona.

OTOH, asignación no dinámica (ver asignación automática) no necesita ser manejado por separado. Una vez que la variable se sale del alcance, la memoria de la pila se recupera y se reutiliza según sea necesario.


1
2017-12-31 19:22



No necesita liberar / liberar las entradas recuperadas por readdir(). Utiliza un búfer interno estático y, por lo tanto, no necesita liberarse dinámicamente porque no está asignado dinámicamente. Tenga en cuenta que el compilador puede predecir el espacio necesario porque solo usa una entrada a la vez y solo necesita una entrada para almacenar los resultados. Por eso tampoco es reentrante. Hay un readdir_r() que toma un búfer asignado por el usuario y, por supuesto, es reentrante.

Tienes que llamar closedir() sobre el DIR * puntero para liberar los recursos utilizados por opendir().


1
2017-12-31 19:27



los struct dirent es lógicamente parte de la DIR. Puede ser reutilizado en una posterior. readdir() llamar al mismo DIR (pero no en un readdir() llamar a un diferente DIR) y será liberado closedir().

Algunos documentos dicen que readdir() no es seguro para subprocesos En todas las implementaciones, excepto en las realmente exóticas, es seguro para subprocesos siempre que el último acceso al anterior struct dirent sucede, antes del siguiente readdir() llamada. Utilizando readdir_r() no es aconsejable porque es muy difícil determinar el límite NAME_MAX correctamente y la readdir_r() La función no conoce el valor utilizado por su llamante.

Más detalles sobre los problemas con readdir_r() están en http://austingroupbugs.net/view.php?id=696.


0
2017-12-31 22:51