إن مسألة ما إذا كان من الضروري التحقق من المردود mallocأمر مثير للجدل ويولد دائمًا نقاشًا ساخنًا.
يعتقد بعض الناس أننا يجب أن نحاول معالجة جميع أنواع أخطاء وقت التشغيل ، بما في ذلك. و OOM المواقف. يعتقد البعض الآخر أنه لا يزال هناك القليل مما يمكن عمله مع OOM ومن الأفضل ترك التطبيق يتعطل. على جانب المجموعة الثانية من الأشخاص ، هناك أيضًا حقيقة أنه من الصعب اختبار منطق معالجة OOM الإضافي. وإذا لم يتم اختبار الكود ، فمن المؤكد أنه لا يعمل.
أوافق تمامًا على أنه لا يجب عليك تنفيذ منطق معالجة الأخطاء الذي لن تختبره. يكاد يكون من المؤكد أنها لن تحسن أي شيء ، وربما أسوأ - تدمير كل شيء.
إن مسألة ما إذا كان يجب محاولة التعامل مع مواقف OOM في المكتبات / التطبيقات أمر مثير للجدل ولن نتطرق إليه هنا. كجزء من هذا المنشور ، أريد فقط أن أشارك تجربتي حول كيفية اختبار المنطق المطبق للتعامل مع مواقف OOM في التطبيقات المكتوبة بلغة C / C ++. ستكون المحادثة حول أنظمة تشغيل Linux و macOS. لعدد من الأسباب ، سيتم تجاوز Windows.
المقدمة
نتمنى جميعًا ألا تحدث OOM أبدًا ، ولكن في الحياة الواقعية لا يكون هذا ممكنًا دائمًا للأسباب التالية:
- ذاكرة الوصول العشوائي محدودة دائمًا.
- لا يتم تمكين SWAP دائمًا.
- لا تتصرف التطبيقات دائمًا بشكل كافٍ وتحاول أحيانًا تخصيص كميات كبيرة غير واقعية من الذاكرة ، وتتداخل مع نفسها ومع الآخرين.
- لا تزال تطبيقات 32 بت موجودة.
- لا يتم تمكين الإفراط في الالتزام دائمًا.
- يمكن تقييد استهلاك الذاكرة باستخدام
ulimit، على سبيل المثال. - من خلال
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++
mallocfree.
- .
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 " ". , , , .
-
printfmacOSSIGSEGV/SIGBUS. - ,
std::bad_alloc, . , , , OOM.std::terminate. . -
std::threadstd::terminatemacOS.
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 , .
:
activateOverthrowerdeactivateOverthrower
:
#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),delayduration.
<--- delay --->
--------------+ +------------------------------
| |
| | All further allocations pass
| |
+----------------+
<--- duration --->
:
OVERTHROWER_STRATEGYOVERTHROWER_SEEDOVERTHROWER_DUTY_CYCLEOVERTHROWER_DELAYOVERTHROWER_DURATION
activateOverthrower. , Overthrower , /dev/urandom.
- Overthrower
malloc/. - .
- Overthrower .
- Overthrower .
- Overthrower , .
- Overthrower’ .
- Overthrower Overthrower-aware . , .
- تم اختبار Overthrower نفسه على Ubuntu (منذ 14.04) و macOS (منذ Sierra (10.12) و Xcode 8.3). أثناء الاختبار ، يحاول Overthrower إسقاط نفسه ، من بين أمور أخرى.
- إذا ظهرت OOM حقيقية في النظام ، فإن Overthrower يبذل قصارى جهده حتى لا يسقط نفسه.