على خيارات برنامج تشغيل Linux ، أو كيف قضيت عطلة نهاية الأسبوع

"نحن كسول وفضولي"




هذه المرة ، كان سبب النشر مقالة في مجلة جيدة مخصصة لنظام التشغيل Linux (يشار إليها فيما بعد باسم L) ، حيث أشاد "الخبير" الجذاب بالسائق الذي يربط شاشة LCD بلوحة Raspbery. نظرًا لأن مثل هذه الأشياء (الاتصال ، وليس نظام التشغيل) تدخل في نطاق اهتماماتي المهنية ، نظرت إلى المقالة باهتمام ، ثم وجدت النص الفعلي لـ "برنامج التشغيل" وفوجئت قليلاً بأنه يمكن الإشادة بتكنولوجيا المعلومات. حسنًا ، بشكل عام ، يمكن تحديد مستوى الخبير فقط لأنه وصف البرنامج بعناد على أنه برنامج تشغيل ، على الرغم من أنه ليس بأي حال من الأحوال. يبدو ، والتين معه ، أنك لا تعرف أبدًا ما يكتبه أي شخص لنفسه ، ولكن لنشر ذلك في المجال العام - "لم أكن أعرف أنه كان ممكنًا".

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

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

ولكن ، قبل أن تنظر إلى المصدر ، سنفكر أولاً قليلاً ، لكن كيف سنفعل ذلك إذا كانت لدينا مهمة مماثلة (وفجأة ، بعد هذا المنشور ، سيدعوني إلى عمال المناجم L ، ولن ترفض). لذلك ، من الممكن إنشاء وحدة نمطية - وحدة برامج معينة مصممة خصيصًا يمكن تحميلها في الذاكرة للتنفيذ باستخدام بعض أدوات النظام (insmode - فيما يلي I) ، بينما يتم تمرير سلسلة من الأحرف كمعلمات إطلاق. يمكن أن يشتمل هذا السطر على وحدات معجمية محددة بدقة ، ويتم تحديد وصف التنسيق لها عند إنشاء النص المصدر للوحدة النمطية ، وتحتوي هذه الوحدات على معلومات تتيح لك تغيير قيمة المتغيرات الداخلية لهذه الوحدة.

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

لتكوين الوحدة النمطية التي نحتاجها:

  1. شكل (حسنًا ، هذا في مرحلة الترجمة ، يمكنك القيام بذلك بالطريقة التي تريدها ، على الرغم من أنها لا تزال مثيرة للاهتمام كيف) وتخزين جدول من الإعدادات أعلاه ،
  2. تحليل معلمات الإدخال وفقًا لهذا الجدول و
  3. قم بإجراء تغييرات على مناطق معينة من الذاكرة وفقًا لنتائج تحليل وحدة نحوية.

سوف نفكر قليلاً في أسلوب "لو كنت المخرج" ونتوصل إلى تطبيقات محتملة. كيف يمكننا تنفيذ السلوك المماثل لأداة النظام المساعدة والوحدة - سنبدأ في تحليل الخيارات بتزايد التعقيد.

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

لذلك ، يجب تضمين محلل قياسي معين تلقائيًا في نص الوحدة النمطية ، وهذا سهل التنفيذ على مستوى استبدال الماكرو.

هذا الحل له عيبان:

  1. ليس من الواضح سبب احتياجنا إليها على الإطلاق. ويمكنك على الفور الاتصال بالوحدة باستخدام معلمات من سطر الأوامر ،
  2. يجب أن يحتوي رمز الوحدة النمطية (جزء التهيئة) على جميع الأقسام الثلاثة من المعلومات الضرورية ، وهذه المعلومات ضرورية فقط عند بدء تشغيل الوحدة النمطية وعدم استخدامها في المستقبل ، وتستغرق دائمًا المساحة. قم بالحجز على الفور بأن هذه المعلومات تشغل بالضرورة مساحة في الملف ، لكنها قد لا تذهب إلى الذاكرة عند تحميل الوحدة ، إذا تم كل شيء بعناية. من أجل القيام بذلك ، نذكّر بالتوجيهات _init و _initdata (بالمناسبة ، لكن كيف تعمل ، سيتعين علينا معرفة ذلك - هذا هو موضوع المنشور التالي - هل ستتطلع إليه؟). ولكن في الحالة الأخيرة ، من الواضح أن القسمين 2 و 3 من المعلومات الموجودة في الملف لا لزوم لهما ، لأن نفس الكود سيكون موجودًا في العديد من الوحدات ، مما ينتهك مبدأ DRY بشكل ضار.

بسبب أوجه القصور المشار إليها ، فإن تنفيذ هذا الخيار أمر مستبعد للغاية. علاوة على ذلك ، ليس من الواضح سبب قيام الماكرو في ذلك الوقت بتعيين معلومات حول نوع المعلمة ، لأن الوحدة نفسها تعرف جيدًا ما الذي تقوم بتعديله (على الرغم من أنه قد يكون مطلوبًا للمحلل عند التحقق من المعلمات). التقييم العام لاحتمال مثل هذا القرار هو 2-3 في المئة.

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

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

يتمثل الخيار الفرعي للحل الثاني في نقل المعلمات المستخرجة ليس إلى جزء بدء الوحدة ، ولكن مباشرةً إلى جزء العمل الذي تم تحميله ، على سبيل المثال ، عبر ioctl - متطلبات الذاكرة هي نفسها. لدينا فرصة فريدة لتغيير المعلمات "أثناء التنقل" ، والتي لا يتم تنفيذها في إصدارات أخرى. ليس من الواضح تمامًا لماذا قد نحتاج إلى مثل هذه الميزة ، لكنها تبدو جميلة. العيب هو 1) تحتاج إلى حجز جزء من منطقة الوظيفة مقدمًا لطلب غير مستخدم ربما ، و 2) يجب أن يكون رمز التعديل موجودًا في الذاكرة باستمرار. تقدير احتمالية التنفيذ - 5٪.

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

متغير من الحل الثالث - لا يتم تخزين المعلومات حول المعلمات في الوحدة النمطية نفسها ، ولكن في بعض الملفات المساعدة ، ثم ببساطة لا توجد ذاكرة زائدة في الوحدة النمطية. من حيث المبدأ ، يمكن القيام بالشيء نفسه في الإصدار السابق ، عندما تحتوي الوحدة على جزء التكوين ، والذي يتم استخدامه أثناء عملية التمهيد ، ولكن لا يتم تحميله في ذاكرة الوصول العشوائي التي تحتوي على الجزء القابل للتنفيذ الفعلي من الوحدة. مقارنة بالإصدار السابق ، تمت إضافة ملف إضافي وليس من الواضح ما الذي ندفع مقابله ، لكن ربما قاموا بتوجيهات التهيئة قبل الاختراع - 5 بالمائة.

سيتم ترك نسبة 7 في المائة المتبقية للحصول على خيارات أخرى لم أتمكن من التوصل إليها. حسنًا ، والآن بعد أن استنفد خيالنا نفسه (من المؤكد بالتأكيد ، إذا كانت هناك أي أفكار أخرى ، فيرجى طرحها في التعليق) ، سنبدأ في دراسة مصدر L.

بادئ ذي بدء ، لاحظت أنه يبدو أن فن توزيع نصوص المصدر في الملفات قد فُقد جنبًا إلى جنب مع نظام التشغيل ، الذي يصل حجمه إلى 16 كيلو بايت ، نظرًا لأن بنية الدليل ، ترتبط أسماءهم وأسماء ملفاتهم بالمحتوى أكثر قليلاً من لا شيء. نظرًا لوجود مضمنات مضمنة ، فإن الدراسة الكلاسيكية للمصادر التي تم تنزيلها بمساعدة المحرر تتحول إلى مهمة غريبة وستكون غير مثمرة. لحسن الحظ ، هناك أداة جذابة Elixir ، متوفرة على الإنترنت ، والتي تتيح لك إجراء عمليات بحث للسياق ، وهنا تصبح العملية أكثر إثارة للاهتمام ومثمرة. قمت بإجراء بحثي الإضافي على الموقع elixir.bootlin.com. نعم ، هذا الموقع ليس مجموعة رسمية من أجبان kernel ، على عكس kernel.org ، لكن دعنا نأمل أن يكون الكود المصدر لهما متطابقين.

أولاً ، دعونا ننظر إلى ماكرو لتحديد المعلمات - أولاً ، نحن نعرف اسمها ، وثانياً ، يجب أن يكون الأمر أسهل (نعم ، الآن). إنه موجود في ملف moduleparam.h - إنه معقول للغاية ، لكن هذه مفاجأة سارة ، بالنظر إلى ما سنراه لاحقًا. ماكرو

{0}module_param(name,type,perm) 

هو غلاف أكثر

  {0a}module_param_named(n,n,t,p) 

- السكر النحوي للحالة الأكثر شيوعا. في الوقت نفسه ، لسبب ما ، يتم سرد تعداد القيم المسموح بها لأحد المعلمات ، أي نوع المتغير ، في التعليقات قبل نص المجمع ، وليس الماكرو الثاني ، الذي يقوم بالفعل بالمهمة ويمكن استخدامه مباشرة.

يحتوي الماكرو {0a} على استدعاء لثلاث وحدات ماكرو

 {1}param_check_##t(n,&v) 

(هناك مجموعة من وحدات الماكرو لجميع الأنواع الصالحة)

 {2}module_param_cb(n,&op##t,&v,p) 

و

 {3}__MODULE_PARM_TYPE(n,t) 

(الانتباه إلى الأسماء ، ومع ذلك ، سحر) ، ويتم استخدام الأول منها في أي مكان آخر ، أي أن توصيات Occam ومبدأ KISS يتم إهمالهما بجرأة من قبل المبدعين من A - على ما يبدو ، نوع من العمل الأساسي للمستقبل. بالطبع ، هذه مجرد وحدات ماكرو ، لكنها لا تكلف شيئًا ، لكنها لا تزال ....

يقوم أول من وحدات الماكرو الثلاثة {1} ، كما يوحي الاسم ، بالتحقق من مراسلات أنواع المعلمات واللفات

 __param_check(n,p,t) 

لاحظ أنه في المرحلة الأولى من الالتفاف ، ينخفض ​​مستوى تجريد الماكرو ، وفي الثانية ، من المحتمل أن يزداد بطريقة مختلفة ، ويبدو لي فقط أنه يمكن أن يكون أكثر بساطة ومنطقية ، لا سيما بالنظر إلى عدم استخدام الماكرو المتوسط ​​في أي مكان آخر. حسنًا ، دعنا نضع طريقة أخرى للتحقق من معلمات الماكرو في البنك الخنزير والمضي قدمًا.

ولكن وحدات الماكرو التالية جهازي إنشاء عنصر في جدول المعلمة بالفعل. لماذا لا تسألني اثنين ، وليس واحدًا ، لقد توقفت منذ فترة طويلة عن فهم منطق منشئي L. على الأرجح ، استنادًا إلى الاختلاف في أسلوب هاتين الماكروتين ، بدءًا من الأسماء ، تمت إضافة الثانية منهما لاحقًا لتوسيع الوظيفة ، وتعديل الهيكل الحالي كان ذلك مستحيلاً ، لأنهم في البداية أعربوا عن أسفهم لتخصيص مكان للإشارة إلى خيار المعلمات. الماكرو {2} ، كما هو الحال دائمًا ، يقنع الماكرو منا

 {2a}_module_param_call(MODULE_PARAM_PREFIX,n,ops,arg,p,-1,0) 

(من المضحك ألا يتم استدعاء هذا الماكرو مباشرةً في أي مكان باستثناء 8250_core.c ، حيث يتم استدعاؤه باستخدام نفس المعلمات الإضافية) ، ولكن الأخير ينتج بالفعل الكود المصدري.

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

نتيجة عمل {1} في نص الوحدة المترجمة في وجود السطرين التاليين

 module_param_named(name, c, byte, 0x444); module_param_named(name1, i, int, 0x444); 

يظهر جزء من النوع أدناه

 static const char __param_str_name[] = "MODULE" "." "name"; static struct kernel_param const __param_name \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name, ((struct module *)0), &param_ops_byte, (0x444), -1, 0, { &c } }; static const char __UNIQUE_ID_nametype72[] \ __attribute__((__used__)) __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name" ":" "byte"; static const char __param_str_name1[] = "MODULE" "." "name1"; static struct kernel_param const __param_name1 \ __attribute__((__used__)) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_name1, ((struct module *)0), &param_ops_int, (0x444), -1, 0, { &i } }; static const char __UNIQUE_ID_name1type73[] __attribute__((__used__)) \ __attribute__((section(".modinfo"), unused, aligned(1))) \ = "parmtype" "=" "name1" ":" "int"; 

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

يبدو الجمع بين السمات __used__ وغير المستخدمة في نفس الوقت مضحكا في آخر سطر تم إنشاؤه ، خاصة إذا نظرت إلى الجزء التالي من التعليمات البرمجية للماكرو

 #if GCC_VERSION < 30300 # define __used __attribute__((__unused__)) #else # define __used __attribute__((__used__)) #endif 

ما هو نوع من خفة الحركة التي يطوّرها مطورو A ، وهي الطريقة الشاقة المؤلمة لأفكارهم التي يجسدها الكود. أعلم أنه يمكنك استخدام كلا شكلي كتابة السمة ، لكن لماذا تفعل ذلك على نفس السطر - لا أفهم.

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

ملاحظة أخرى - لم يكن تحديد ما يتحول إليه تعريف المعلمة تمامًا مهمة تافهة تمامًا ، ولكن بفضل MinGW ، كان لا يزال مكتملًا. وتحت غطاء محرك السيارة ، كان هناك توحيد وتقييد مزدوج للمعلمات ، وتشكيل أسماء فريدة ، بالإضافة إلى حيل أخرى صعبة من العمل مع وحدات الماكرو ، لكنني لا أعرض سوى النتائج. بتلخيص النتيجة الوسيطة ، أستطيع أن أقول إن دراسة وحدات الماكرو A ليست ما أود كسب رزقه ، إنها ممكنة كترفيه فقط ، لكننا نستمر.

لن نتقدم في تحسين فهم وحدات الماكرو في فهم المهمة ، لذلك ننتقل إلى الكود المصدري لـ And ونحاول فهم ما تقوم به.

بادئ ذي بدء ، نحن مندهشون لرؤية أن الأجبان المطلوبة ليست مدرجة في مصادر النواة. نعم ، أنا مستعد للموافقة على أن الأداة المساعدة I وتتفاعل مع kernel من خلال نقطة الدخول لتحميل الوحدة ، لكن أي كتاب على برامج التشغيل L يخبرنا عن هذه الأداة المساعدة ، وبالتالي فإن عدم وجود نسخة "رسمية" من مصدرها في مكان ما بالقرب من مصدر أسباب kernel سوء فهم لي. حسنًا ، حسنًا ، لم تخذلنا شركة Google ، وقد خرجنا جميعًا بالجبن.

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

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

نجد مجموعتين تحتويان على النص المصدر ، ونجد أيضًا الجبن على جيثب ، ونرى أنهما متطابقان ونعتقد أن هذه هي الطريقة التي تبدو بها شفرة المصدر للأداة. بعد ذلك ، ندرس فقط الملف على git ، خاصةً لأنه هنا فقط يسمى insmod.c ، نجد أنه وللبداية ، يحول قائمة المعلمات إلى سلسلة طويلة منتهية بقيمة خالية ، حيث يتم فصل العناصر الفردية بمسافات. بعد ذلك ، يقوم بالاتصال بوظيفتين ، أولهما يسمى grub_file ويفتح بوضوح الثنائي ، في حين أن الثاني له اسم init_module ويأخذ مؤشر إلى ملف مفتوح مع الوحدة النمطية الثنائية وسلسلة من المعلمات ويسمى load_module ، والذي يوحي الغرض من هذه الوظيفة بالتحميل مع تعديل المعلمات.

ننتقل إلى نص الوظيفة الثانية ، التي تكمن في الملف ... وهنا المشكله - ليس في أي من ملفات مستودع الدراسة على Geet (حسناً ، هذا منطقي فقط ، هذا جزء من النواة وموقعه ليس هنا). جوجل مرة أخرى في عجلة من امرنا للمساعدة ويعيدنا إلى الجبن النواة تحت Elixir وملف module.c. تجدر الإشارة إلى أنه من المثير للدهشة أن اسم الملف الذي يحتوي على وظائف العمل مع الوحدات النمطية يبدو منطقيًا ، حتى أنني لا أفهم كيف أشرح ذلك ، ربما حدث ذلك عن طريق الصدفة.

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

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

ملاحظة - هناك حصاة أخرى في حديقة البحث المضمّنة - عند البحث عن تعريف لهذا الماكرو ، رأيت العديد من الأماكن لاستخدامه كدالة ، ومن بينها تعريفه على أنه ماكرو بشكل متواضع للغاية.

على سبيل المثال ، لا أفهم سبب الحاجة إلى أداة خارجية لترجمة المعلمات من النموذج القياسي لنظام التشغيل (agrc ، argv) إلى شكل سلسلة منتهية بقيمة خالية مع وجود فراغات ، والتي تتم معالجتها بشكل إضافي بواسطة وحدة النظام - هذا النهج يفوق بعض الشيء القدرات المعرفية. خاصةً ، بالنظر إلى حقيقة قيام المستخدم بإدخال سلسلة معلمة في شكل سلسلة منتهية بصفر مع مسافات فواصل ، وتحولها الأداة المساعدة في kernel إلى نموذج (argc ، argv). تذكرنا بقوة بالنكتة القديمة "نحن نزيل الغلاية من الموقد ، ونسكب الماء منه ونواجه مشكلة معروفة بالفعل." وبما أنني أحاول التقيد بمبدأ "لا تنظر إلى محادثك أكثر غباء من نفسك ، حتى يثبت العكس. وحتى بعد ذلك ، يمكن أن تكون مخطئًا ، "وفيما يتعلق بمطوري A ، فإن العبارة الأولى صالحة بالتأكيد ، فهذا يعني أنني أسيء فهم شيء ما ، لكنني لست معتادًا على ذلك. إذا كان بإمكان أي شخص تقديم تفسير معقول للحقيقة المعلنة المتمثلة في التحويل المزدوج ، فأنا أطلب في التعليق. لكننا نواصل التحقيق.

تصبح احتمالات تنفيذ الخيارين 1 و 2 "ضعيفة للغاية" (صياغة ساحرة من مقال حديث عن احتمالات تطوير ADC عالي السرعة المحلي) ، حيث سيكون من الغريب تحميل وحدة نمطية إلى الذاكرة باستخدام وظيفة kernel ، ثم تمرير التحكم إليها لتنفيذ kernel وظيفة بنيت في جسمه. وبالتأكيد ، في نص دالة load_module ، نجد بسرعة استدعاء parse_args - يبدو أننا نسير على الطريق الصحيح. بعد ذلك ، ننتقل سريعًا عبر سلسلة الاتصال (كما هو الحال دائمًا ، سنرى وظائف المجمع ووحدات الماكرو المجمعة ، لكننا معتادون بالفعل على غض الطرف عن مثل هذه المزحات اللطيفة للمطورين) ونجد وظيفة parse_one ، التي تضع المعلمة المطلوبة في المكان المناسب.

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

ومع ذلك ، فقد تجاهلنا تمامًا مسألة كيفية وصول وظائف التحليل إلى مجموعة من عينات المعلمات ، لأنه بدون هذا ، يكون التحليل صعبًا إلى حد ما. تُظهر نظرة سريعة على الكود أنه تم تطبيق الاختراق القذر ، خدعة واضحة - في الملف الثنائي ، تبحث الدالة find_module_sections عن القسم المسمى __param ، وتقسم حجمها على حجم السجل (تقوم بالكثير) وتُرجع البيانات الضرورية عبر الهيكل. ما زلت أضع الحروف p أمام أسماء المعلمات لهذه الوظيفة ، لكن هذه مسألة ذوق.

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

تلخيص - كانت عطلة نهاية الأسبوع مفيدة ، كان من المثير للاهتمام فهم الكود المصدري لـ L ، وتذكر شيء ما وتعلم شيء ما ، لكن المعرفة لا لزوم لها.
حسنًا ، في افتراضاتي ، لم أكن أعتقد أنه في L تم تنفيذ خيار تبين أنه في الـ 7 بالمائة المتبقية ، لكنه لم يكن واضحًا بشكل مؤلم.

حسنًا ، في الختام ، صرخة ياروسلافنا (كيف يمكن الاستغناء عنها) لماذا يجب أن أبحث عن المعلومات الضرورية (لا أقصد المطبخ الداخلي ، بل العرض الخارجي) من مصادر مختلفة ليس لها وضع رسمي ، حيث توجد وثيقة مشابهة للكتاب
"برنامج كمبيوتر. نظام التشغيل الوظيفي.
رافوس. دليل مبرمج النظام. "، أم لم يعد؟

Source: https://habr.com/ru/post/ar431860/


All Articles