La cuestión de si es necesario comprobar qué retornos malloc
es controvertida y siempre genera un acalorado debate.
Algunas personas piensan que deberíamos intentar manejar todo tipo de errores de tiempo de ejecución, incluido. y situaciones OOM. Otros creen que todavía es poco lo que se puede hacer con OOM y que es mejor dejar que la aplicación se bloquee. Del lado del segundo grupo de personas, también está el hecho de que la lógica de procesamiento OOM adicional es difícil de probar. Y si el código no se prueba, es casi seguro que no funcione.
Estoy totalmente de acuerdo en que no debe implementar la lógica de manejo de errores que no va a probar. Es casi seguro que no mejorará nada, y tal vez incluso peor: arruinará todo.
La cuestión de si intentar o no manejar situaciones OOM en bibliotecas / aplicaciones es controvertida y no la tocaremos aquí. Como parte de esta publicación, solo quiero compartir mi experiencia de cómo puede probar la lógica implementada para manejar situaciones OOM en aplicaciones escritas en C / C ++. La conversación versará sobre los sistemas operativos Linux y macOS. Por varias razones, se omitirá Windows.
Introducción
Todos deseamos que OOM nunca sucedió, pero en la vida real esto no siempre es posible debido a las siguientes razones:
- La RAM siempre es limitada.
- SWAP no siempre está habilitado.
- Las aplicaciones no siempre se comportan de manera adecuada y, a veces, intentan asignar cantidades de memoria grandes y poco realistas, lo que interfiere con ellas mismas y con los demás.
- Todavía existen aplicaciones de 32 bits.
- la sobrecomisión no siempre está habilitada.
- El consumo de memoria se puede limitar usando
ulimit
, por ejemplo. -
LD_PRELOAD
.
, , , OOM . , , :
- , - .
- OOM . .
- . , .
, , SQLite. , . . SQLite .
, , , . OOM Killer, , . , C++, .
1.
, OOM . my_malloc
my_free
malloc
free
.
my_free
. my_realloc
.
my_malloc
malloc
. my_malloc
, NULL
.
, :
- 3rd party .
-
malloc
. -strdup
. -
malloc
’ , , . - C++
malloc
free
.
- .
2.
Linux LD_PRELOAD
. . malloc
. , malloc
/realloc
/free
(weak
). , macOS LD_PRELOAD
, DYLD_INSERT_LIBRARIES
.
, , LD_PRELOAD
DYLD_INSERT_LIBRARIES
malloc
/realloc
NULL
.
, "" . , .
, "" , , . :
-
main
. , . - Runtime macOS " ". , , , .
-
printf
macOSSIGSEGV
/SIGBUS
. - ,
std::bad_alloc
, . , , , OOM.std::terminate
. . -
std::thread
std::terminate
macOS.
UPDATE: Travis CI , macOS / Xcode , std::bad_alloc
, std::thread
std::terminate
.
Overthrower. - malloc
NULL
. Overthrower - , .
3.
main
, main
, main
runtime . main
, , .. - main
.
, main
. Overthrower, OOM . Overthrower , .
:
activateOverthrower
deactivateOverthrower
:
#ifdef __cplusplus
extern "C" {
#endif
void activateOverthrower() __attribute__((weak));
unsigned int deactivateOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif
Overthrower LD_PRELOAD
, NULL
, , .
, , :
int main(int argc, char** argv)
{
activateOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
activateOverthrower
/deactivateOverthrower
, :
TEST(Foo, Bar)
{
activateOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
, -, , Overthrower , :
#ifdef __cplusplus
extern "C" {
#endif
void pauseOverthrower(unsigned int duration) __attribute__((weak));
void resumeOverthrower() __attribute__((weak));
#ifdef __cplusplus
}
#endif
:
TEST(Foo, Bar)
{
activateOverthrower();
// Some code we want to test ...
pauseOverthrower(0);
// Some fragile code we can not fix ...
resumeOverthrower();
// Some code we want to test ...
deactivateOverthrower();
}
__cxa_allocate_exception
, , , malloc
, NULL
. , Linux, malloc
, __cxa_allocate_exception
(emergency buffer), , . .
macOS , , , , std::bad_alloc
, std::terminate
.
UPDATE: , macOS / Xcode .
, , , __cxa_allocate_exception
malloc
. - Overthrower’ malloc
. Overthrower malloc
__cxa_allocate_exception
.
, , , macOS __cxa_atexit
, Linux dlerror
. .
Overthrower , malloc
free
. Overthrower’ , activateOverthrower
deactivateOverthrower
, :
overthrower got deactivation signal.
overthrower will not fail allocations anymore.
overthrower has detected not freed memory blocks with following addresses:
0x0000000000dd1e70 - 2 - 128
0x0000000000dd1de0 - 1 - 128
0x0000000000dd1030 - 0 - 128
^^^^^^^^^^^^^^^^^^ | ^^^^^^ | ^^^^^^^^^^
pointer | malloc | block size
|invocation|
| number |
Overthrower , , .
Overthrower’, , valgrind. , OOM. , , Overthrower . Overthrower , , deactivateOverthrower
, stderr
.
Overthrower 3 :
- Random —
rand() % duty_cycle == 0
.duty_cycle
, . - Step — (
malloc_seq_num >= delay
),delay
.
<--- delay --->
--------------+
|
| All further allocations fail
|
+------------------------------
- Pulse — (
malloc_seq_num > delay && malloc_seq_num <= delay + duration
),delay
duration
.
<--- delay --->
--------------+ +------------------------------
| |
| | All further allocations pass
| |
+----------------+
<--- duration --->
:
OVERTHROWER_STRATEGY
OVERTHROWER_SEED
OVERTHROWER_DUTY_CYCLE
OVERTHROWER_DELAY
OVERTHROWER_DURATION
activateOverthrower
. , Overthrower , /dev/urandom
.
- Overthrower
malloc
/. - .
- Overthrower .
- Overthrower .
- Overthrower , .
- Overthrower’ .
- Overthrower Overthrower-aware . , .
- Overthrower en sí se prueba en Ubuntu (desde 14.04) y macOS (desde Sierra (10.12) y Xcode 8.3). Durante las pruebas, Overthrower intenta soltarse, entre otras cosas.
- Si aparece un OOM real en el sistema, Overthrower hace todo lo posible para no caerse.