GNU/Linux >> Tutoriales Linux >  >> Linux

Preguntas sobre putenv() y setenv()

  • [El] putenv(char *string); llamada parece fatalmente defectuosa.

Sí, es fatalmente defectuoso. Se conservó en POSIX (1988) porque ese era el estado de la técnica. El setenv() mecanismo llegó más tarde. Corrección: El estándar POSIX 1990 dice en §B.4.6.1 "Funciones adicionales putenv() y clearenv() fueron considerados pero rechazados". La versión 2 de Single Unix Specification (SUS) de 1997 enumera putenv() pero no setenv() o unsetenv() . La siguiente revisión (2004) definió tanto setenv() y unsetenv() también.

Debido a que no copia la cadena pasada, no puede llamarla con un local y no hay garantía de que una cadena asignada al montón no se sobrescriba o elimine accidentalmente.

Tienes razón en que una variable local es casi invariablemente una mala elección para pasar a putenv() — las excepciones son oscuras hasta el punto de casi no existir. Si la cadena se asigna en el montón (con malloc() et al), debe asegurarse de que su código no lo modifique. Si lo hace, está modificando el entorno al mismo tiempo.

Además (aunque no lo he probado), dado que un uso de las variables de entorno es pasar valores al entorno del niño, esto parece inútil si el niño llama a uno de los exec*() funciones ¿Me equivoco en eso?

El exec*() Las funciones hacen una copia del entorno y la pasan al proceso ejecutado. Ahí no hay problema.

La página de manual de Linux indica que glibc 2.0-2.1.1 abandonó el comportamiento anterior y comenzó a copiar la cadena, pero esto provocó una pérdida de memoria que se solucionó en glibc 2.1.2. No me queda claro qué fue esta pérdida de memoria o cómo se solucionó.

La pérdida de memoria surge porque una vez que ha llamado putenv() con una cadena, no puede usar esa cadena nuevamente para ningún propósito porque no puede saber si todavía está en uso, aunque podría modificar el valor sobrescribiéndolo (con resultados indeterminados si cambia el nombre al de una variable de entorno encontrado en otra posición en el medio ambiente). Entonces, si tiene espacio asignado, el clásico putenv() lo filtra si cambia la variable nuevamente. Cuando putenv() comenzó a copiar datos, las variables asignadas dejaron de estar referenciadas porque putenv() ya no mantuvo una referencia al argumento, pero el usuario esperaba que el entorno lo hiciera referencia, por lo que se filtró la memoria. No estoy seguro de cuál fue la solución:3/4 esperaría que volviera al comportamiento anterior.

setenv() copia la cadena, pero no sé exactamente cómo funciona. El espacio para el entorno se asigna cuando se carga el proceso, pero es fijo.

El espacio del entorno original es fijo; cuando empiezas a modificarlo, las reglas cambian. Incluso con putenv() , el entorno original se modifica y podría crecer como resultado de agregar nuevas variables o como resultado de cambiar las variables existentes para que tengan valores más largos.

¿Hay alguna convención (¿arbitraria?) en el trabajo aquí? Por ejemplo, ¿asignar más ranuras en la matriz de punteros de cadena env de las que se usan actualmente y mover el puntero de terminación nulo hacia abajo según sea necesario?

Eso es lo que el setenv() es probable que lo haga el mecanismo. La variable (global) environ apunta al inicio de la matriz de punteros a las variables de entorno. Si apunta a un bloque de memoria a la vez y a un bloque diferente en un momento diferente, entonces el entorno cambia, así de simple.

¿La memoria para la nueva cadena (copiada) está asignada en el espacio de direcciones del propio entorno y, si es demasiado grande para caber, simplemente obtiene ENOMEM?

Bueno, sí, podrías obtener ENOMEM, pero tendrías que esforzarte bastante. Y si aumenta demasiado el entorno, es posible que no pueda ejecutar otros programas correctamente; el entorno se truncará o la operación de ejecución fallará.

Teniendo en cuenta los problemas anteriores, ¿hay alguna razón para preferir putenv() sobre setenv()?

  • Utilice setenv() en código nuevo.
  • Actualice el código antiguo para usar setenv() , pero no lo conviertas en una prioridad principal.
  • No utilice putenv() en código nuevo.

No hay un espacio especial "el entorno":setenv simplemente asigna espacio dinámicamente para las cadenas (con malloc por ejemplo) como lo haría normalmente. Debido a que el entorno no contiene ninguna indicación de dónde proviene cada cadena, es imposible que setenv o unsetenv para liberar cualquier espacio que pueda haber sido asignado dinámicamente por llamadas anteriores a setenv.

"Debido a que no copia la cadena pasada, no puede llamarla con un local y no hay garantía de que una cadena asignada al montón no se sobrescriba o elimine accidentalmente". El propósito de putenv es asegurarse de que si tiene una cadena asignada en montón, es posible eliminarla a propósito . Eso es lo que el texto de justificación quiere decir con "la única función disponible para agregar al entorno sin permitir fugas de memoria". Y sí, puede llamarlo con un local, simplemente elimine la cadena del entorno (putenv("FOO=") o unsetenv) antes de regresar de la función.

El punto es que usar putenv hace que el proceso de eliminar una cadena del entorno sea completamente determinista. Mientras que setenv en algunas implementaciones existentes modificará una cadena existente en el entorno si el nuevo valor es más corto (para evitar siempre pérdida de memoria), y dado que hizo una copia cuando llamó a setenv, no tiene el control de la cadena originalmente asignada dinámicamente, por lo que no puede liberarla cuando se elimine.

Mientras tanto, setenv sí mismo (o unsetenv) no puede liberar la cadena anterior, ya que, incluso ignorando putenv, la cadena puede provenir del entorno original en lugar de haber sido asignada por una invocación anterior de setenv.

(Toda esta respuesta asume un putenv implementado correctamente, es decir, no el de glibc 2.0-2.1.1 que mencionaste).


Linux
  1. Las 30 principales preguntas y respuestas de entrevistas de OpenStack

  2. Las 25 preguntas y respuestas principales de la entrevista de Linux

  3. 20 Preguntas y respuestas de la entrevista Postfix

  4. BIND:preguntas y respuestas de la entrevista del servidor DNS

  5. 10 datos interesantes y divertidos sobre Linux

20 preguntas y respuestas de la entrevista de Red Hat Satellite Server

Cómo establecer y enumerar variables de entorno en Linux

10 datos divertidos sobre Linus Torvalds y Linux

Cómo configurar y desactivar variables de entorno en Linux

Mejor entorno de escritorio Linux:15 revisados ​​y comparados

Las mejores combinaciones de distribución de Linux y entorno de escritorio