صورة

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

الرابط الأصلي: https://mairacanal.github.io/does-the-linux-kernel-need-software-engineering/

إخلاء المسؤولية: هذه المقالة هي ترجمة CSDN ، يرجى الإشارة إلى مصدر إعادة الطباعة.


المؤلف |  قناة ميرا

مترجم | صن رونان محرر | تو مين

مُنتجة | CSDN (المعرف: CSDNnews)

هل تتطلب نواة Linux هندسة برمجيات؟ لأولئك الذين يبحثون عن إجابة قصيرة: نعم ، إنها كذلك.

الآن ، يمكننا الغوص في إجابة أكثر تفصيلاً.

هندسة البرمجيات هي نهج أكثر منهجية لتطوير البرمجيات يتضمن التعريف والتنفيذ الهندسي والاختبار والإدارة والتحسين وتحسين دورة حياة البرنامج. عند التفكير في البرامج من هذا المنظور ، يجب علينا أيضًا مراعاة متطلبات والتصميم والبناء والاختبار والصيانة.

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

مسرد لبعض مصطلحات هندسة البرمجيات:

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

قابلية التوسع: مقياس لمدى سهولة توسيع أو تقليص عنصر البرنامج.

قابلية الاختبار: مقياس لمدى سهولة اختبار أداة برمجية.

قد يعتقد الكثير من الناس أن هندسة البرمجيات على نواة لينكس مستحيلة ، أو حتى غير مطلوبة على الإطلاق. يبدو لي أن هذه المعتقدات تأتي من روايتين:

  • من المستحيل تطبيق C على هندسة البرمجيات: في بعض الأحيان ترتبط هندسة البرمجيات فقط بلغات البرمجة الموجهة للكائنات.

  • عند استخدام برامج التشغيل ، لا يلزم هندسة برمجيات: نظرًا لأن برامج التشغيل محدودة نظريًا ، فلا يتعين علينا التفكير في قابلية تطويرها وقابليتها للصيانة.

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

الأسوأ من ذلك كله: عندما تحتوي قاعدة التعليمات البرمجية الضخمة على الكثير من التعليمات البرمجية السيئة ، تصبح قابلية الصيانة صعبة ، وتزداد جودة البرامج سوءًا.

لذا ، دعونا نفهم أولاً سبب خطأ هذين المعتقدين.

صورة

هندسة البرمجيات باستخدام لغة سي

قد تسأل: كيف تستخدم أنماط التصميم الفاخرة لتجنب تكرار الكود وتحقيق تعدد الأشكال الجميل في حالة عدم وجود فئات؟

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

على الرغم من أن لغة C هي لغة منظمة ، إلا أنه يمكن استخدامها لكتابة برامج موجهة للكائنات. وبهذا المعنى ، فإن المكتبات والهياكل هي "حليفك" الرئيسي لهندسة البرمجيات في C. بالإضافة إلى ذلك ، يمكنك استخدام مؤشرات الوظائف لإنشاء تعدد الأشكال في C.

على سبيل المثال ، إذا كنت تريد كتابة قائمة انتظار بسيطة في لغة C ، فيمكنك استخدام الطريقة التالية:

#ifndef QUEUE_H_#define QUEUE_H_
typedef struct Queue Queue;struct Queue { int *buffer; int head; int size; int tail; int (*isFull)(Queue* const me); int (*isEmpty)(Queue* const me); int (*getSize)(Queue* const me); void (*insert)(Queue* const me, int k); int (*remove)(Queue* const me);};
/* Constructor and destructors */void Queue_Init(Queue const me, (*isFullFunction)(Queue* const me), (*isEmptyFunction)(Queue* const me), (*getSizeFunction)(Queue* const me), (*insertFunction)(Queue* const me, int k), (*removeFunction)(Queue* const me));
void Queue_Cleanup(Queue* const me);
/* Operations */int Queue_isFull(Queue* const me);int Queue_isEmpty(Queue* const me);int Queue_getSize(Queue* const me);void Queue_insert(Queue* const me, int k);int Queue_remove(Queue* const me);
Queue *Queue_Create(void);void Queue_Destroy(Queue* const me);
#endif

لاحظ أن تعدد الأشكال يتم تنفيذه بهذه الطريقة لأنه يمكنني إنشاء بنية جديدة ترث قائمة الانتظار ، مثل:

typedef struct CachedQueue CachedQueue;struct CachedQueue {  Queue *queue;
/* new attributes */ char name[80]; int numberElementsOnDisk;
/* aggregation in subclass */ Queue *outputQueue;
/* inherited virtual function */ int (*isFull)(CachedQueue* const me); int (*isEmpty)(CachedQueue* const me); int (*getSize)(CachedQueue* const me); void (*insert)(CachedQueue* const me, int k); int (*remove)(CachedQueue* const me);
/* new virtual functions */ void (*flush)(CachedQueue* const me); int (*load)(CachedQueue* const me);};

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

كما ترون ، يمكن عمل هندسة البرمجيات الفاخرة في الواقع بلغة C. هناك بعض التجريدات الأنيقة في Linux التي تستخدم هذه المفاهيم ، مثل Virtual File System (VFS). أيضًا ، توفر بعض المكتبات واجهة برمجة تطبيقات لطيفة مثل نظام DRM الفرعي.

لكن في بعض الأحيان لا يعمل هذا مع تنفيذ السائق. يقودنا هذا إلى المشكلة التالية: يجب تصميم برامج التشغيل بشكل صحيح.


صورة
يجب تصميم برامج التشغيل كبرامج


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

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

لنبدأ بفرضية أن "برامج التشغيل محدودة": يمكنك الحصول على ورقة البيانات ، وكود الجهاز حتى نهاية الوظيفة وإكمال برنامج التشغيل.

ومع ذلك ، لا تقوم شركات الأجهزة عادةً بتطوير منتج واحد بخصائص فريدة: عادةً ما تقوم بإنشاء خط إنتاج ، وفي بعض الأحيان يكون لخط الإنتاج "منتجات فرعية" (منتجات فرعية) ، أي يتم ترقية المنتج بشكل متكرر.

خطوط الإنتاج لديها "أطفال" ... هذا يبدو وكأنه حالة رائعة من الميراث لمبرمجي OOP.

لذا ، إذا كان لديك خط إنتاج ، فهل تنشئ ملفًا لكل منتج؟ بالنسبة للمنتج الذي يضيف بعض الميزات الجديدة ، هل يمكنك لصق برنامج التشغيل السابق وتعديل بضع مئات من أسطر التعليمات البرمجية؟ لا يبدو هذا اختيارًا جيدًا للأسباب التالية:

  • رمز مكرر: أنت أيضًا تقوم بتكرار التعليمات البرمجية والأخطاء ؛
  • تغطية الاختبار: الاختبار المتكرر مطلوب ؛
  • قابلية الصيانة: في مرحلة صيانة المشروع ، كلما قل الرمز كلما كان ذلك أفضل ؛ 

كما ترى ، كل ذلك يتلخص في قابلية الصيانة.

كمثال جيد على إعادة استخدام الكود ، يمكنك إلقاء نظرة على النظام الفرعي IIO. غالبًا ما يكون لدى الشركات المصنعة للأجهزة مثل Maxim و Analog Devices Inc شرائح تشترك في نفس خريطة التسجيل أو الوظائف المشتركة. بدلاً من إنشاء برامج تشغيل لكل شريحة ، يكتب المطورون برنامج تشغيل ويضيفون معرّفات الأجهزة المتوافقة إلى جدول الجهاز. على سبيل المثال ، يمكنك التحقق من برنامج التشغيل Maxim MAX1027 ADC ، المتوافق مع MAX1027 و MAX1029 و MAX1031 و MAX1227 و MAX1229 و MAX1231. لذلك لدينا سائق واحد لستة أجهزة: هذا رائع لقابلية الصيانة!

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

ومع ذلك ، في الواقع ، عند فتح مجلد DML في AMD Display Core ، يكون ملف display_mode_vba في DCN20 و DCN21 أكثر دقة. خطوط الإنتاج متشابهة جدًا ، لذلك قد نتمكن من إعادة استخدام الكثير من التعليمات البرمجية.

ومع ذلك ، إذا قمت بفحص هذا الدليل ، يمكنك أن ترى أن هناك ثلاثة ملفات مختلفة: display_mode_vba_20.c و display_mode_vba_20v2.c و display_mode_vba_21.c.

تحقق من الاختلافات بين الملفات عن طريق:

برامج الفرق $ / GPU / DRM / AMD / العرض / DC / DML / DCN20 / Display_mode_vba_20.c

السائقين / GPU / DRM / AMD / العرض / DC / DML / DCN20 / Display_mode_vba_20v2.c

ومعظم الكود هو نفسه: أعني أن بعض الوظائف لا تغير سطرًا واحدًا! يمكن أن يكون لهذا تأثير كبير على قابلية الصيانة.

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

إذا كان بإمكاني تخمين سبب قيام AMD بنسخ الكود ولصقه عدة مرات ، فسأشير إلى مشكلة صيانة أخرى: الوظائف ضخمة! تتجاوز الوظائف في بعض ملفات VBA ألف سطر.

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

من الناحية المثالية ، وفقًا لمبادئ كتاب Clean Code ، نود أن يكون لدينا بعض الوظائف التي توفر البساطة وتوفر وظيفة واحدة. أعلم: هذا لا يعمل بنسبة 100٪ من الوقت ، لكن لا يمكنني العثور على سبب وجيه لكون الوظيفة ضخمة جدًا ولديها عشرات الحجج.

بالإضافة إلى سهولة القراءة ، فإن هذه الوظائف الضخمة تسبب أيضًا قدرًا كبيرًا من الضرر للمكدس.

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

مسرد لبعض مصطلحات هندسة البرمجيات:

قابلية القراءة: مقياس لمدى سهولة قراءة أداة برمجية.

الفهم : مقياس لمدى سهولة فهم منتج البرنامج.

لكنها ليست طريق مسدود لرمز AMDGPU DML . أعني ، تعمل برامج تشغيل AMDGPU بشكل مثالي على Linux ، وإعادة هيكلة الكود دائمًا خيار.


صورة
يمكننا التفكير في البرمجيات!
في هذه المرحلة ، يمكن استنتاج أنه نظرًا لأن برنامج تشغيل AMDGPU مفتوح المصدر ، فيمكننا إصلاح هذه المشكلات في الكود. لكن ليس من الآمن تفكيك الكود ببساطة وإعادة كتابته في مجموعة التصحيح ، لأن برامج تشغيل AMDGPU يجب أن تظل تعمل على نظام Linux.

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

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

الآن ، مع وظائف أصغر ، من الأفضل مشاركة التعليمات البرمجية على DCN وإنشاء واجهة مشتركة لها.

تتيح إعادة البناء هذه استخدام أنماط التصميم التي تحدثت عنها سابقًا ، مما يجعل DML أكثر قابلية للصيانة والقراءة. يمكننا التفكير في استخدام الوراثة ، مع مكتبة أساسية يمكن أن يمتد منها DCN20 ، ومن ثم يمكن أن يمتد DCN21 من DCN20. هذه هي الطريقة التي تصبح بها هذه الملفات الثلاثة الكبيرة ملفات صغيرة.

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

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

- اقتراحات للقراءة -
☞ جائزة السلوك المشوش: "غرس" شريحة ذات نواة صلبة في يدك ، فقط لفتح السيارة؟
اعتذر مؤتمر Tencent مرتين على التوالي: تمت استعادة فشل تسجيل الدخول ؛ كسر عملاق التجارة الإلكترونية في جنوب شرق آسيا Shopee العقد على نطاق واسع ؛ تم إصدار Deno 1.25 | عناوين Geek
Microsoft تكشف عن أثقل برنامج في التاريخ: مترجم C / C ++ يصل وزنه إلى 36 رطلاً!

صورة