2011-10-30 710 views
17

Leí que en los sistemas Unix, malloc puede devolver un puntero que no sea NULL, incluso si la memoria no está realmente disponible, e intentar usar la memoria más adelante provocará un error. Como no puedo detectar ese error al buscar NULL, me pregunto qué tan útil es verificar NULL en absoluto.¿Puedo confiar en que malloc devolverá NULL?

En una nota relacionada, Herb Sutter dice que manejar los errores de memoria C++ es inútil, porque el sistema sufrirá espasmos de paginación mucho antes de que realmente ocurra una excepción. ¿Esto también se aplica al malloc?

+3

Creo que no debe usar malloc en C++: http://stackoverflow.com/questions/184537/in-what-cases-do-i-use-malloc-vs-new – lc2817

+1

@ lc2817 solo debe usar malloc si está escribiendo código con una interfaz C (es decir, funciones que se utilizarán desde C pero escritas en C++) ** y ** el código C es responsable de liberar esa memoria. –

+0

@WTP gracias por esta precisión. Aunque, no sé si es el caso aquí. – lc2817

Respuesta

32

Citando Linux manuals:

Por defecto, Linux sigue una estrategia de asignación de memoria optimista. Esto significa que cuando malloc() devuelve no NULL, no hay garantía que la memoria esté realmente disponible. Este es un error realmente malo. En caso de que el sistema se quede sin memoria, uno o más procesos serán asesinados por el infame asesino OOM. En el caso de Linux se emplea en circunstancias en las que sería menos deseable para perder repentinamente algunos al azar procesos escogidos, y por otra parte la versión del núcleo es suficientemente reciente, se puede desactivar este comportamiento La sobrecarga usando un comando como:

# echo 2 > /proc/sys/vm/overcommit_memory 

Debe comprobar el retorno NULL, especialmente en sistemas de 32 bits, ya que el espacio de direcciones del proceso podría agotarse mucho antes de la RAM: en Linux de 32 bits, por ejemplo, los procesos de usuario pueden tener espacio de direcciones utilizable de 2G 3G a diferencia de más de 4G de RAM total. En sistemas de 64 bits podría ser inútil verificar el código de retorno malloc, pero podría considerarse una buena práctica de todos modos, y hace que su programa sea más portátil. Y, recuerde, desreferenciar el puntero nulo mata su proceso ciertamente; algunos intercambios pueden no doler mucho en comparación con eso.

Si sucede malloc para volver NULL cuando se trata de asignar sólo una pequeña cantidad de memoria, entonces hay que tener cuidado cuando se trata de recuperarse de la condición de error como cualquier posterior malloc puede fallar también, hasta que hay suficiente memoria disponible.

El operador de C++ predeterminado new es a menudo un contenedor sobre los mismos mecanismos de asignación empleados por malloc().

+8

+1 para citar una buena diatriba sobre cómo el valor predeterminado de Linux ** está roto **. Un buen programa siempre debe verificar el valor de retorno de 'malloc'. Si el usuario ha configurado incorrectamente su sistema (o lo dejó en una configuración predeterminada rota), entonces, por supuesto, esto puede no ser de ayuda, pero no hay nada que pueda hacer y el bloqueo está fuera de su responsabilidad. Pero si no comprueba el valor de retorno de 'malloc', su programa se cerrará incluso cuando se ejecute en sistemas donde el usuario/administrador ** realmente se preocupa por la corrección ** y ha deshabilitado el compromiso excesivo. El usuario probablemente considerará la basura de su programa. :-) –

+2

Bueno, la verdad es un poco más complicado que eso. Hay agujeros en el espacio de direcciones del proceso; por ejemplo, el programa puede no tocar todas las páginas en BSS, o cambiar una página que está mapeada en el segmento de datos. Una falta de compromiso suele ser un problema mayor en un sistema de escritorio/servidor que un compromiso excesivo. Y la partición de intercambio, si está habilitada, proporciona algo de amortiguación antes de que las cosas se pongan realmente feas. –

+0

No estoy de acuerdo. Undercommit no es un problema porque siempre puedes lanzar más swap en él. En cualquier caso, si tiene páginas de bss/data intactas, eso significa que tiene variables globales (no solo GOT/PLT allí), que es un problema mayor. :-) Tal vez sean necesarias algunas, pero más de una o dos páginas seguramente indican problemas de diseño ... –

5

En Linux, puede de hecho no se basan en malloc regresar NULL si hay suficiente memoria no está disponible debido a la estrategia de asignación excesiva del núcleo, pero aún debe comprobar que debido a que en algunas circunstancias mallocse retorno NULL, por ejemplo, cuando solicita más memoria de la disponible en la máquina en total. La página de manual de Linux malloc(3) llama a la sobreasignación "un error realmente malo" y contiene consejos sobre cómo desactivarlo.

Nunca he escuchado que este comportamiento también se produzca en otras variantes de Unix.

En cuanto a los "espasmos de paginación", eso depende de la configuración de la máquina. Por ejemplo, tiendo a no configurar una partición de intercambio en las instalaciones de Linux para portátiles, ya que el comportamiento exacto que teme podría matar el disco duro. Todavía me gustaría que los programas C/C++ que ejecuto verifiquen los valores de retorno malloc, proporcionen los mensajes de error apropiados y, cuando sea posible, limpien ellos mismos.

+1

Overcommit no es ni una característica ni un error, estrictamente hablando. Era simplemente una pereza histórica: el compromiso excesivo es mucho más fácil de implementar que la contabilidad de la carga de compromiso. Es de suponer que algunas personas se acostumbraron y les gustó (por razones perversas) y algunos incluso comenzaron a escribir programas que 'malloc' 1gb como una matriz dispersa y cosas aún más perversas, por lo que ahora estamos atrapados con que es por-por- predeterminado ... –

1

Para ver esto desde un punto de vista alternativo:

"malloc puede devolver un puntero no nulo, incluso si la memoria no está realmente disponible" no significa que siempre devuelve un valor no nulo. Puede haber (y habrá) casos en los que se devuelve NULL (como ya se dijo), por lo que este control es necesario.

2

La comprobación de la devolución de malloc no le ayuda mucho por sí solo para que sus asignaciones sean más seguras o menos propensas a errores. Incluso puede ser una trampa si esta es la única prueba que implementa.

Cuando se invoca con un argumento de 0, la norma permite malloc devolver un tipo de dirección única, que no es un puntero nulo y al que no tiene derecho de acceso, sin embargo. Por lo tanto, si solo prueba si el resultado es 0 pero no prueba los argumentos en malloc, calloc o realloc, es posible que encuentre un segfault mucho más tarde.

Esta condición de error (memoria agotada) es bastante rara en entornos "alojados". Por lo general, tienes problemas mucho antes de que te molestes con este tipo de error. (Pero si está escribiendo librerías en tiempo de ejecución, es un kernel hacker o un generador de cohetes, esto es diferente, y allí la prueba tiene perfecto sentido)

Las personas tienden a decorar su código con capturas complicadas de esa condición de error que abarcan varios líneas, haciendo perror y cosas así, que pueden tener un impacto en la legibilidad del código.

Creo que este "comprobar el retorno de malloc" está muy sobreestimado, a veces incluso defendido de forma bastante dogmática. Otras cosas son mucho más importantes:

  • siempre inicializar variables, siempre. para las variables del puntero esto es crucial, deje que el programa se cuelgue bien antes de que las cosas se pongan feas. los miembros punteros no inicializados en struct s son una causa importante de errores que son difíciles de encontrar.
  • Compruebe siempre el argumento para malloc y compañía si esto es una constante de tiempo de compilación como sizof toto no puede haber un problema, pero siempre asegurarse de que su vector de asignación maneja el caso cero correctamente.

Una cosa fácil de comprobar si hay retorno de malloc es envolverlo con algo como memset(malloc(n), 0, 1). Esto solo escribe un 0 en el primer byte y se bloquea muy bien si malloc tuvo un error o n fue 0 para empezar.

+0

Digamos que es mucho más agradable decirle al usuario "Fuera de montón en línea foo" que solo "excepción de puntero nulo en la barra"; para ello sería suficiente un envoltorio simple (¿macro?) para malloc. Esto en caso de que uno use cantidades ridículas de memoria y pueda esperar usar más de 2G en sistemas de 32 bits. –

Cuestiones relacionadas