كيف يتعامل نظام التشغيل بشكل عام مع إدارة ذاكرة kernel ومعالجة الصفحات؟

StackOverflow https://stackoverflow.com/questions/271115

سؤال

أنا أعمل على تصميم النواة، ولدي بعض الأسئلة المتعلقة بالترحيل.

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

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

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

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

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

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

شكرا على أي نصيحة.

هل كانت مفيدة؟

المحلول

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

أولاً ، حول استدعاء وظائف kernel. لا يكفي مجرد الحصول على الوظائف في مكان ما يمكن أن يتصل به البرنامج ، لأن البرنامج على الأرجح يعمل في "وضع المستخدم" (الحلقة 3 على IA-32) ويجب أن يعمل kernel في "وضع kernel" (عادةً Ring 0 على IA-32) للقيام بعملياتها المميزة. عليك أن تفعل بطريقة ما الانتقال بين الوضعين ، وهذا هو بنية محددة للغاية.

في IA-32 ، الطريقة التقليدية هي استخدام بوابة في IDT مع مقاطعة البرنامج (يستخدم Linux Int 0x80). لدى المعالجات الأحدث طرق أخرى (أسرع) للقيام بذلك ، وأيها متوفرة تعتمد على ما إذا كانت وحدة المعالجة المركزية من AMD أو Intel ، وعلى نموذج وحدة المعالجة المركزية المحددة. لاستيعاب هذا الاختلاف ، تستخدم نواة Linux الأخيرة صفحة من التعليمات البرمجية التي تم تعيينها بواسطة kernel في الجزء العلوي من مساحة العنوان لكل عملية. لذلك ، في Linux الأخيرة ، لاستدعاء النظام ، اتصل بوظيفة على هذه الصفحة ، والتي بدورها ستفعل كل ما هو مطلوب للتبديل إلى وضع kernel (يحتوي kernel على أكثر من نسخة واحدة من تلك الصفحة ، واختصار النسخ المراد استخدامها على التمهيد اعتمادا على ميزات المعالج الخاص بك).

الآن ، إدارة الذاكرة. هذا ال تسربت موضوعات؛ يمكنك كتابة كتاب كبير عنه وليس هذا الموضوع.

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

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

تخصص البرامج الذاكرة على بقية مساحة العنوان بعدة طرق:

  • يتم تخصيص جزء من الذاكرة بواسطة برنامج تحميل برنامج kernel. يتضمن ذلك رمز البرنامج (أو "النص") ، والبيانات المهيئة للبرنامج ("البيانات") ، والبيانات غير المكتملة للبرنامج ("BSS" ، ومليء الصفر) ، والمكدس ، والعديد من الاحتمالات والغايات. مقدار التخصيص ، حيث ، ما ينبغي أن يكون المحتويات الأولية ، التي يتم استخدام أعلام الحماية التي يجب استخدامها ، والعديد من الأشياء الأخرى ، من الرؤوس على الملف القابل للتنفيذ المراد تحميله.
  • تقليديًا على UNIX ، هناك مساحة من الذاكرة يمكن أن تنمو وتتقلص (يمكن تغيير الحد الأعلى لها عبر brk() استدعاء النظام). يتم استخدام هذا تقليديًا من قبل الكومة (مخصص الذاكرة على مكتبة C ، والتي malloc() هي واحدة من الواجهات ، هي المسؤولة عن الكومة).
  • في كثير من الأحيان يمكنك أن تطلب من kernel تعيين ملف إلى منطقة عنوان العنوان. تقرأ وتكتب إلى تلك المنطقة (عبر الترحيل السحر) الموجهة إلى ملف الدعم. هذا عادة ما يسمى mmap(). مع مجهول mmap, ، يمكنك تخصيص مناطق جديدة من مساحة العنوان التي لا يتم دعمها بواسطة أي ملف ، ولكنها تتصرف بنفس الطريقة. غالبًا ما يستخدم محمل برنامج kernel mmap لتخصيص أجزاء من رمز البرنامج (على سبيل المثال ، يمكن دعم رمز البرنامج من خلال التنفيذ نفسه).

تعتبر مناطق Acessing من مساحة العنوان التي لا يتم تخصيصها بأي شكل من الأشكال (أو محفوظة للنواة) خطأ ، وسيؤدي إلى إرسال إشارة إلى البرنامج.

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

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

نصائح أخرى

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

في الإصدار x86، هناك طريقتان لتنفيذ مكالمات النظام:مع المقاطعات، ومع تعليمات sysenter/sysexit.مع المقاطعات، تقوم النواة بإعداد ملف جدول واصف المقاطعة (IDT)، الذي يحدد نقاط الدخول المحتملة إلى النواة.يمكن بعد ذلك استخدام رمز المستخدم int تعليمات لإنشاء مقاطعة ناعمة للاتصال بالنواة.يمكن أيضًا إنشاء المقاطعات بواسطة الأجهزة (ما يسمى بالمقاطعات الصعبة)؛يجب أن تكون هذه المقاطعات بشكل عام مختلفة عن المقاطعات الناعمة، لكن ليس من الضروري أن تكون كذلك.

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

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

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top