يبدو أن لدي عادة الكتابة عن
الآلات القوية ، حيث العديد من النوى
عاطلة بسبب الأقفال غير الصحيحة. نعم ... نعم. مرة أخرى عن ذلك.
هذه القصة مثيرة للإعجاب بشكل خاص. في الواقع ، كم عدد المرات التي تدور فيها خيط واحد لبضع ثوانٍ في دورة من سبعة أوامر ، مع الاستمرار في قفل يوقف عمل 63 معالجات أخرى؟ إنه لأمر مدهش ، بمعنى رهيب.
خلافًا للاعتقاد السائد ، لا أمتلك فعليًا 64 معالجات منطقية ، ولم أر هذه المشكلة على الإطلاق. لكن صديقي صدمها ،
طالبني الطالب الذي يذاكر كثيرا ، وطلب المساعدة ، وقررت أن المشكلة كانت مثيرة للغاية. لقد أرسل
تتبعًا ETW مع معلومات كافية حتى يتمكن العقل الجماعي على Twitter من حل المشكلة بسرعة.
كانت شكوى الصديق بسيطة للغاية: لقد جمع
الإنشاء باستخدام
النينجا . عادةً ما يقوم ninja بعمل رائع لزيادة الحمل ، حيث يدعم باستمرار عمليات n + 2 لتجنب التوقف. ولكن هنا ، بدا استخدام وحدة المعالجة المركزية في أول 17 ثانية من التجميع كما يلي:

إذا ألقيت نظرة فاحصة (نكتة) ، يمكنك رؤية خط رفيع حيث ينخفض إجمالي حمل وحدة المعالجة المركزية من 100٪ إلى 0٪ في بضع ثوانٍ. في نصف ثانية فقط ، يتم تقليل الحمل من 64 إلى اثنين أو ثلاثة مؤشرات ترابط. فيما يلي جزء مكبّر لأحد هذه السقوط - يتم وضع علامة على الثواني على طول المحور الأفقي:

كان الفكر الأول أن النينجا لا يمكن أن يخلق عمليات بسرعة. لقد رأيت هذا عدة مرات ، عادة بسبب تدخل برنامج مكافحة الفيروسات. ولكن عندما قمت بفرز الرسوم البيانية بحلول وقت الانتهاء ، وجدت أنه خلال مثل هذه الحوادث لم تكتمل أي عمليات ، لذا فإن النينجا ليست مسؤولة.
يعد جدول
استخدام وحدة المعالجة المركزية (دقيق) مثاليًا لتحديد سبب التوقف. يتم تخزين سجلات جميع مفاتيح السياق هناك ، بما في ذلك السجلات الدقيقة لكل بداية من التدفق ، بما في ذلك المكان والوقت.
الحيلة هي أنه لا يوجد شيء خاطئ مع التوقف. تنشأ المشكلة عندما نريد حقًا أن يؤدي الخيط العمل ، ولكن بدلاً من ذلك يكون الخمول. لذلك ، تحتاج إلى تحديد لحظات معينة من التوقف.
عند التحليل ، من المهم فهم أن تبديل السياق يحدث عندما يستأنف مؤشر الترابط العملية. إذا نظرنا إلى تلك الأماكن عندما يبدأ تحميل المعالج في الانخفاض ، فلن نجد أي شيء. بدلاً من ذلك ، ركّز على الوقت الذي بدأ فيه النظام بالعمل من جديد. هذه المرحلة تتبع هي أكثر دراماتيكية. بينما يستغرق انخفاض حمل وحدة المعالجة المركزية نصف ثانية ، تستغرق العملية العكسية من مؤشر ترابط واحد يستخدم إلى تحميل كامل اثني عشر مللي ثانية فقط! الرسم البياني أدناه كبير للغاية ، ومع ذلك ، فإن الانتقال من الخمول إلى التحميل يكاد يكون خطًا رأسيًا:

لقد قمت بالتكبير إلى اثني عشر مللي ثانية ووجدت 500 مفتاح تبديل للسياق ، يلزم إجراء تحليل دقيق هنا.
يحتوي جدول تبديل السياق على العديد من الأعمدة التي قمت
بتوثيقها هنا . عندما تتجمد العملية ، من أجل العثور على السبب ، أقوم بتجميع حسب العمليات الجديدة ، أو سلاسل العمليات الجديدة ، أو مجموعات سلاسل الرسائل الجديدة ، وما إلى ذلك (
تمت مناقشتها هنا ) ، لكن هذا لا يعمل على مئات العمليات التي تم إيقافها. إذا درست أي عملية خاطئة واحدة ، فمن الواضح أنه تم إعدادها من قبل العملية السابقة ، التي أعدتها العملية السابقة ، وأود أن مسح سلسلة طويلة للعثور على الرابط الأول الذي (يفترض) يحمل قفل مهم لفترة طويلة.
لذلك ، جربت تصميم عمود مختلف في البرنامج:
- وقت التبديل (عند حدوث تبديل للسياق)
- عملية الاستعداد (من أطلق القفل بعد الانتظار)
- عملية جديدة (من بدأ العمل)
- الوقت منذ الأخير (إلى متى تنتظر العملية الجديدة)
هذا يعطي قائمة مرتبة حسب الوقت لمفاتيح التبديل للسياق مع ملاحظة من الذي أعد من والى متى كانت العمليات جاهزة للعمل.
اتضح أن هذا يكفي. الجدول أدناه يتحدث عن نفسه إذا كنت تعرف كيفية قراءته. مفاتيح تبديل السياق القليلة الأولى ليست ذات أهمية ، لأن وقت الانتظار لعملية جديدة (الوقت منذ آخر) صغير جدًا ، ولكن في السطر المميز (# 4) يبدأ شيء مثير للاهتمام:

يقول هذا السطر أن
النظام (4) أعد
cl. Exe (3032) ، والذي انتظر 3.368 ثانية. يقول السطر التالي إنه في أقل من 0.1 مللي ثانية ، أعد
cl. Exe (3032) cl.exe (16232) ، والذي انتظر 3.367 ثانية. و هكذا.
لا يتم تضمين عدة مفاتيح تبديل للسياق ، كما في السطر رقم 7 ، في سلسلة الانتظار ، ولكنها تعكس ببساطة أعمالًا أخرى في النظام ، ولكن بشكل عام يتم توسيع السلسلة لتشمل العديد من العناصر.
هذا يعني أن كل هذه العمليات تنتظر إصدار نفس القفل. عندما تطلق عملية
النظام (4) القفل (بعد التمسك لمدة 3،368 ثانية!) ، تقوم عمليات الانتظار ، بدورها ، بالتقاطه والقيام بعملهم القليل وتمرير القفل. تحتوي قائمة انتظار الانتظار على حوالي مائة عملية ، مما يدل على درجة تأثير قفل واحد.
أظهرت دراسة صغيرة لـ
Ready Thread Stacks أن معظم التوقعات تأتي من
KernelBase.dllWriteFile . لقد طلبت من WPA عرض المتصلين بهذه الوظيفة ، مع التجميع. هناك يمكنك أن ترى أنه في 12 مللي ثانية من هذا التنفيس ، 174 سلسلة تخرج عن انتظار
WriteFile ، وانتظرت في المتوسط 1،184 ثانية:
174 المواضيع في انتظار WriteFile ، متوسط وقت الانتظار 1،184 ثانيةهذا تأخر مدهش ، وفي الواقع ، لا حتى النطاق الكامل للمشكلة ، لأن العديد من مؤشرات الترابط من وظائف أخرى ، مثل
KernelBase.dll! GetQueuedCompletionStatus ، تتوقع إصدار نفس القفل.
ماذا يفعل النظام (4)
في هذه المرحلة ، عرفت أن تقدم الإنشاء قد توقف لأن جميع عمليات التحويل البرمجي وغيرها كانت تتوقع
WriteFile ، نظرًا لأن
النظام (4) احتفظ بالقفل. أظهر عمود
معرف مؤشر ترابط
جاهز آخر أن مؤشر الترابط 3276 أصدر القفل في عملية النظام.
أثناء جميع عمليات تعليق التجميع ، تم تحميل مؤشر الترابط 3276 بنسبة 100٪ ، لذلك فمن الواضح أنه قام ببعض الأعمال على وحدة المعالجة المركزية أثناء الضغط على القفل. لمعرفة مكان قضاء وقت وحدة المعالجة المركزية ، دعونا نلقي نظرة على
الرسم البياني لاستخدام وحدة المعالجة المركزية (العينات) لمؤشر الترابط 3276. كانت بيانات استخدام وحدة المعالجة المركزية واضحة بشكل مدهش. تقريبًا كل الوقت يستغرق عمل دالة واحدة
ntoskrnl.exe! RtlFindNextForwardRunClear (يتم الإشارة إلى عدد العينات في العمود مع الأرقام):
مكدس الاستدعاءات يؤدي إلى ntoskrnl.exe! RtlFindNextForwardRunClearعرض مكدس مؤشر الترابط
Read Read Thread ID أكد أن
NtfsCheckpointVolume أصدر القفل بعد 3.368 ثانية:
استدعاء المكدس من NtfsCheckpointVolume إلى ExReleaseResourceLiteفي هذه اللحظة ، بدا لي أن الوقت قد حان لاستخدام المعرفة الغنية من متابعي على Twitter ، لذلك قمت بنشر
هذا السؤال وأظهرت مكالمة مكالمة كاملة. يمكن أن تكون تغريدات بهذه الأسئلة فعالة للغاية إذا قدمت معلومات كافية.
في هذه الحالة ، جاءت
الإجابة الصحيحة من
Caitlin Gadd بسرعة كبيرة ، إلى جانب العديد من الاقتراحات العظيمة الأخرى. لقد قمت بإيقاف تشغيل ميزة استرداد النظام - وفجأة ، ذهب البناء مرتين إلى ثلاث مرات أسرع!
ولكن مهلا ، ما هو أفضل
يعد حظر التنفيذ في جميع أنحاء النظام لمدة 3 ثوانٍ أكثر إثارة للإعجاب ، ولكن الموقف أكثر إثارة للإعجاب إذا أضفت عمود
العنوان إلى جدول
استخدام وحدة المعالجة المركزية (أخذ العينات) وفرزها. إنه يظهر بالضبط أين تحصل عينات
RtlFindNextForwardRunClear - و 99٪ منهم يقعون في تعليمة واحدة!

أخذت
ملفات ntoskrnl.exe و
ntkrnlmp.pdb (نفس الإصدار كصديق لي) وركضت
dumpbin /disasm
لعرض وظيفة الاهتمام في المجمّع. تختلف الأرقام الأولى من العناوين لأن الشفرة تتحرك عند الإقلاع ، لكن القيم السداسية الأربع الأخيرة هي نفسها (لا تتغير بعد ASLR):
RtlFindNextForwardRunClear:
...
14006464F: 4C 3B C3 cmp r8 ، rbx
140064652: 73 0F jae 0000000140064663
140064654: 41 39 28 cmp dword ptr [r8] ، ebp
140064657: 75 0A jne 0000000140064663
140064659: 49 83 C0 04 add r8.4
14006465D: 41 83 C1 20 add r9d، 20h
140064661: EB EC jmp 000000014006464F
...
نرى أن التعليمات على ... تم تضمين 4657 في دورة من سبعة تعليمات ، والتي توجد في عينات أخرى. يشار إلى عدد هذه العينات على اليمين:
RtlFindNextForwardRunClear:
...
14006464F: 4C 3B C3 cmp r8 ، rbx 4
140064652: 73 0F jae 0000000140064663 41
140064654: 41 39 28 cmp dword ptr [r8] ، ebp
140064657: 75 0A jne 0000000140064663 7498
140064659: 49 83 C0 04 add r8.4 2
14006465D: 41 83 C1 20 add r9d، 20h 1
140064661: EB EC jmp 000000014006464F 1
...
كتمرين للقارئ ، دعنا نترك تفسير عدد العينات على معالج فائق المستوى مع التنفيذ غير العادي للتعليمات ، على الرغم من أنه يمكن العثور على بعض الأفكار الجيدة في
هذه المقالة . في هذه الحالة ، لدينا 3290WX AMD Ryzen Threadripper. على ما يبدو ، فإن وظيفة المعالج Micro-Up Fusion مع تنفيذ خمس تعليمات في وقت واحد تسمح فعليًا بإكمال كل دورة في jne ، نظرًا لأن التعليمات بعد التعليمة الأكثر تكلفة تقع في غالبية الانقطاعات في التحديد.
لذلك اتضح أن الجهاز الذي يحتوي على 64 من المعالجات المنطقية يتم إيقافه بواسطة دورة من سبعة أوامر في عملية النظام ، مع الاحتفاظ بقفل NTFS الحيوي ، والذي يتم تصحيحه عن طريق تعطيل استرداد النظام.
المقطع الختامي
ليس من الواضح سبب تصرف هذا الرمز بشكل سيء على هذا الجهاز المحدد. أفترض أن هذا مرتبط بطريقة ما بتوزيع البيانات على قرص 2 تيرابايت فارغ تقريباً. عندما تم تشغيل استرداد النظام ، عادت المشكلة أيضًا ، ولكن ليست شديدة. ربما هناك نوع من الأمراض للأقراص مع شظايا ضخمة من مساحة فارغة؟
ذكر متابع آخر على Twitter خلل Volume Shadow Copy من Windows 7 ، والذي يسمح
بالتنفيذ أثناء O (n ^ 2) . تم إصلاح هذا الخطأ في نظام التشغيل Windows 8 ، ولكن ربما تم الحفاظ عليه بشكل ما. تُظهر تتبعات المكدس الخاصة بي بوضوح أن
VspUpperFindNextForwardRunClearLimited (العثور على بت مستخدم في هذه المساحة 16 ميجابايت) تستدعي
VspUpperFindNextForwardRunClear (تبحث عن البت المستخدم التالي في أي مكان ، لكن لا
تُرجعه إذا كان خارج المنطقة المحددة). بالطبع ، هذا يسبب بعض الشعور deja vu. كما قلت
مؤخرًا ، O (n ^ 2) هي نقطة ضعف خوارزميات قابلة للتطوير بشكل سيئ. يتزامن هنا عاملان: مثل هذا الرمز بسرعة كافية للدخول في الإنتاج ، ولكنه بطيء بدرجة كافية لإسقاط هذا الإنتاج.
كانت هناك تقارير تفيد بحدوث مشكلة مماثلة مع
حذف الملفات على
نطاق واسع ، لكن تتبعنا لا يُظهر العديد من عمليات الحذف ، وبالتالي فإن المشكلة ، على ما يبدو ، ليست كذلك.
في الختام ، سأكرر جدول تحميل وحدة المعالجة المركزية على مستوى النظام من بداية المقالة ، لكن هذه المرة تشير إلى استخدام وحدة المعالجة المركزية بواسطة عملية مشكلة
النظام (باللون الأخضر أدناه). في مثل هذه الصورة ، المشكلة واضحة تمامًا. تكون عملية النظام مرئية تقنيًا على الرسم البياني العلوي ، لكن من السهل على هذا النطاق تفويتها.

على الرغم من أن المشكلة واضحة على الرسم البياني ، إلا أنها لا تثبت شيئًا في الواقع.
كما يقولون ، العلاقة ليست علاقة سببية. يُظهر تحليل أحداث تبديل السياق فقط أن هذا الدفق هو الذي يحمل القفل الحرج - ومن ثم يمكنك التأكد من أننا وجدنا السبب الفعلي ، وليس مجرد ارتباط عشوائي.
طلبات
كالعادة ، أختتم هذا التحقيق
بدعوة لتسمية الخيوط بشكل أفضل . تحتوي عملية النظام على عشرات مؤشرات الترابط ، والعديد منها له غرض خاص ، وليس لأي منها اسم. كان مؤشر ترابط النظام الأكثر ازدحامًا في هذا التتبع هو
MiZeroPageThread . غرقت مرارًا وتكرارًا في مكدسها ، وفي كل مرة أتذكر أنها لم تكن موضع اهتمام. لا يقوم برنامج التحويل البرمجي VC ++ بتسمية مؤشرات الترابط الخاصة به. لا يتطلب الأمر الكثير من الوقت لإعادة تسمية الجداول ، وهي مفيدة حقًا. فقط اعط الاسم
انها بسيطة . يتضمن Chromium أداة
لسرد أسماء الدفق في العملية .
إذا أراد شخص من فريق NTFS في Microsoft التحدث عن هذا الموضوع ، فأعلمني بذلك ، ويمكنني توصيلك بمؤلف التقرير الأصلي وتقديم تتبع ETW.
مراجع