اليوم ، تخبرني العملية بخيط لا يريد العيش

هذه هي المادة الأصلية السادسة والستين لبناة جافا

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

الاتصالات

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

فيما يتعلق بالتواصل بين العمليات ، هناك ثلاثة أسئلة هنا

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

تجدر الإشارة إلى أن المسألين الأخيرتين في هذه القضايا الثلاث قابلة للتطبيق أيضًا على المواضيع

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

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

حالة تنافسية

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

لنفترض أن دليل الخلفية لدينا يحتوي على الكثير من مواضع القمار (الفتحة) ، والرقم هو 0 ، 1،2 ، ... ، يتم تخزين كل فتحة في اسم ملف. في الوقت نفسه ، هناك متغيران مشتركان: خارج ، مشيرًا إلى الملف التالي الذي يجب طباعته ؛ في الإشارة إلى الفتحة الخاملة التالية في الدليل. يمكن حفظ هذين الملفان في ملف يمكن الوصول إليه بواسطة جميع العمليات ، وطول الملف هو كلمتين. في لحظة معينة ، يتم احتلال مواقع الفتحة من 0 إلى 3 ، وتشغل الفتحة رقم 4 إلى 6. في الوقت نفسه ، قررت العملية A والعملية B طباعة ملف في السطر. الموقف كما يلي

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

معالجة A يقرأ في قيمة 7 ، وهناك متغير محلي في 7 في متغير محلي next_free_slot. في هذا الوقت ، حدث انقطاع على مدار الساعة ، واعتقدت وحدة المعالجة المركزية أن العملية أ هي وقت طويل بما فيه الكفاية وقررت التبديل إلى العملية ب. اقرأ العملية B أيضًا قيمة IN ، ووجدت أنها كانت في السابعة من عمرها ، ثم كتبت العملية B 7 في متغيرها المحلي NEXT_FREE_SLOT. في هذه اللحظة ، اعتقدت العمليتان أن الفتحة التالية المتاحة كانت 7.

تعمل العملية B الآن ، وسوف تكتب اسم ملف الطباعة في الفتحة 7 ، ثم قم بتغيير مؤشر Pone إلى 8 ، ثم تترك العملية B للقيام بأشياء أخرى للقيام بأشياء أخرى

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

المجال الحيوي

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

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

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

  • في أي وقت ، لا يمكن أن تكون العمليتان في المنطقة الحرجة في نفس الوقت
  • لا تتعامل مع سرعة وعدد وحدة المعالجة المركزية لتقديم أي افتراضات
  • يجب ألا تمنع العملية الموجودة خارج المنطقة الحرجة العمليات الأخرى
  • لا يمكن جعل أي عمليات تنتظر بلا حدود لدخول المنطقة الحرجة
  • من منظور مجردة ، نريد عادة سلوك العملية كما هو موضح في الشكل أعلاه. في وقت T1 ، تدخل A إلى المنطقة الحرجة. في وقت T2 ، تحاول العملية B الدخول إلى المنطقة الحرجة ، لأنها في هذا الوقت ، تكون العملية A في المنطقة الحرجة في المنطقة الحرجة. لذلك ، ستحظر العملية B حتى تترك عملية T3 T3 المنطقة الحرجة. في هذا الوقت ، يمكن أن تسمح العملية B بدخول المنطقة الحرجة. أخيرًا ، في T4 ، تغادر العملية B المنطقة الحرجة ، ويتم استعادة النظام إلى الحالة الأصلية التي لا توجد عملية.

    كن مشغولاً

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

    درع

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

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

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

    قفل المتغير

    0 0 0 1 1 0 0 1

    0 1 0 1 1

    set-before-check

    C C C C++ Java Modula3 Pascal Java native C C++ C Java C C C C++ Java

    0

    while(TRUE){ while(turn != 0){ /* */ critical_region(); turn = 1; /* */ noncritical_region(); }}

    1

    while(TRUE){ while(turn != 1){ critical_region(); turn = 0; noncritical_region(); }}

    turn 0 0 turn 0 1 0 turn 1 (busywaiting) CPU (spinlock)

    0 turn 1 1 1 turn 0 0 turn 1turn 1

    0 turn 1 1 0 while 1 turn 0

    3 يجب ألا تمنع العملية الموجودة خارج المنطقة الحرجة العمليات الأخرى 0

    Peterson

    T.Dekker

    G.L.Peterson

    #define FALSE 0#define TRUE 1#define N 2/* */int turn;/* */int interested;/* 0 (FALSE) */void enter_region(int process){/* 0 1 */ int other;/* */ other = 1 - process;/* */ interested = TRUE;/* */ turn = process; while(turn == process interested == true){} /* */ }void leave_region(int process){ interested == FALSE; /* */}

    0 1 enter_region leave_region

    0 enter_region turn 0 1 enter_region enter_region 1 interested FALSE 0 leave_region

    enter_region turn 1 turn 1 while 0 1 0

    TSL

    TSL RX,LOCK

    (test and set lock) lock RX TSL CPU CPU

    1 2 2 1

    TSL lock lock 0 TSL 1 move lock 0

    enter_region:TSL REGISTER,LOCK | 1 CMP REGISTER,#0 | 0 JNE enter_region | RET | leave_region:MOVE LOCK,#0| 0 RET |

    Peterson 4 lock lock 1 0 0 0 lock

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

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

    Enter_region: نقل السجل ،#1 | وضع 1 في سجل XCHG المشتق من الذاكرة ، قفل | سجل التبادل وقفل المحتوى المتغير CMP ،#0 | قفل 0 هو 0؟ JNE Enter_region | إذا لم يكن 0 ، فقد تم تعيين القفل ، وعاد الدورة RET | إلى المتصل ، أدخل المنطقة الحرجة LEEVE_REGION: نقل القفل ،#0 | stock 0ret في القفل | العودة إلى المتصل

    جوهر XCHG هو نفس حل TSL. تستخدم جميع وحدات المعالجة المركزية Intel X86 تعليمات XCHG في المزامنة السفلية.

    النوم والصحوة

    حل Peterson و TSL و XCHG في الحل أعلاه صحيح ، لكن لديهم جميعًا أوجه القصور في الانتظار. جوهر هذه الحلول هو نفسه. تحقق أولاً مما إذا كان بإمكانه الدخول إلى المنطقة الحرجة. إذا لم يُسمح بذلك ، فستنتظر العملية في مكانها حتى يُسمح بها.

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

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

    أسئلة المنتج المستهلك

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

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

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

    #define n 100/* caps لعدد فتحات فتحات*/int عدد = 0/* عدد بيانات المخزن المؤقت* /// المنتج باطلب المنتج (void) {int item ؛ بينما (صحيح) {/* حلقة غير محدودة */item = product_item ()/* ينشئ البيانات التالية*/if (count == n) {spec () ؛/* إذا كانت منطقة ذاكرة التخزين المؤقت ممتلئة ، فسيتم حظر*/} يتم وضع البيانات الحالية في منطقة المخزن المؤقت*/ count = count + 1 ؛/* يزيد من عدد العدد المخزن المؤقت*/ if (count == 1) {wakeup (المستهلك) ؛/* هل المخزن المؤقت فارغًا؟ */}}} // المستهلك المستهلك المستهلك (void) {int item ؛ about (true) {/* infinite loop*/if (count == 0) {/* إذا كان المخزن المؤقت فارغًا ، فسوف يمنع الانسداد. */ sleep () ؛} item = remove_item () ؛/*خذ بيانات من المخزن المؤقت*/ count = count -1/*يتم تقليل عدد العد في المخزن المؤقت بمقدار*/ if (count == n -1 ) {/* المخزن المؤقت ممتلئ؟ */ wakeup (منتج) ؛} consumer_item (عنصر) ؛/ *طباعة عنصر البيانات */}}

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

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

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

    ومع ذلك ، عندما تكون هناك العديد من العمليات ، يمكنك أن تقول في هذا الوقت من خلال زيادة عدد الانتظار للانتظار ، لذلك هناك 2 ، 4 ، 6 ، 8 Wake -up ، وما إلى ذلك ، لكنه لم يحلها بشكل أساسي بشكل أساسي سؤال.

    الإشارة

    الإشارة هي طريقة اقترحها E.W.Dijkstra في عام 1965. وتستخدم متغير بلاستيكي لإيقاظ عدد المرات للاستخدام لاحقًا. في وجهة نظره ، هناك نوع متغير جديد يسمى Semaphore. يمكن أن تكون قيمة الإشارة إلى 0 ، أو أي رقم إيجابي. 0 يعني أنه لا يلزم أي استيقاظ ، وأي رقم إيجابي يمثل عدد أوقات الاستيقاظ.

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

    "" "

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

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

    استخدم Semaphore لحل مشكلة المستهلك المنتج

    استخدم الإشارة لحل مشكلة الاستيقاظ المفقودة ، والرمز كما يلي

    #define n 100/* حدد عدد أخاديد منطقة العازلة*/kypf int semaphore ؛/* الأعراض هي int*/semaphore mutex = 1 ؛/* التحكم في الوصول إلى المناطق الرئيسية*/semaphore فارغة = n ؛/* إحصائيات من عدد المخزن المؤقت الأخاديد الفارغة*/semaphore كاملة = 0 ؛/*إحصائيات لعدد الأخاديد الكاملة المخزن المؤقت*/void المنتج (void) {int item ؛ about (true (true) {/*true statter is 1*/ item = product_item () ؛/* قم بإنشاء بعض البيانات الموضوعة في المخزن المؤقت*/double (فارغ) ؛/* ناقص عدد الأخاديد الفارغة بمقدار 1*/مزدوج (mutex) ؛/* أدخل المناطق الرئيسية*/insert_item (العنصر ) ؛/* handle */ UP (mutex) ؛/* */ UP (full) ؛/* buffer + 1*/}} void consumer (void) {int العنصر ؛ بينما (صواب) {/* دورة غير محدودة*/لأسفل (كامل) ؛/* منطقة ذاكرة التخزين المؤقت الفتحة الكاملة رقم 1*/لأسفل (mutex) ؛/* خارج البيانات من المخزن المؤقت*/UP (mutex) ؛/* اترك المنطقة الحرجة*/UP (فارغة) ؛/* عدد الفتحات الفارغة+ 1*/strupe_item (عنصر) ؛/* بيانات المعالجة*/}}}} }}}}}}}}}}}}}

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

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

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

    الآن لدينا ضمان جيد للبدائل. ثم دعونا نلقي نظرة على ضمان ترتيب المقاطعة

  • أجهزة الضغط على عداد برنامج المكدس ، إلخ.
  • يتم تحميل الجهاز في عداد البرنامج الجديد من متجه المقاطعة
  • توفر قيمة عملية لغة التجميع قيمة السجل
  • إعداد مكدس جديد لعمليات لغة التجميع
  • C تشغيل خادم المقاطعة (يكتب القراءة النموذجية وذاكرة التخزين المؤقت)
  • يحدد المجدول البرنامج أدناه الذي يعمل أولاً
  • تعود عملية C إلى رمز التجميع
  • تبدأ العملية الجديدة للغة التجميع في تشغيل العملية الحالية الجديدة
  • في نظام به إشارة ، تتمثل الطريقة الطبيعية للانقطاع المخفي في السماح لكل جهاز I/O بتجهيزه برملة ، والتي تم تعيينها في البداية على 0. بعد تنشيط جهاز I/O ، قام برنامج معالجة المقاطعة على الفور بإجراء عملية لأسفل على الإشارة ذات الصلة ، لذلك تم حظر العملية على الفور. عندما تدخل المقاطعة ، يتم تنفيذ برنامج معالجة المقاطعة لاحقًا لعملية UP على Semaphore ذات الصلة ، والتي يمكن أن تعيد العمليات التي تم منعها. في خطوات معالجة المقاطعة المذكورة أعلاه ، تكون عملية خادم المقاطعة الخامسة C هي عملية لأعلى يتم تنفيذها بواسطة برنامج معالجة المقاطعة على الإشارة. لذلك ، في الخطوة 6 ، يمكن لنظام التشغيل إجراء برنامج تشغيل الجهاز. بالطبع ، إذا كانت العديد من العمليات جاهزة بالفعل ، فقد يختار برنامج الجدولة تشغيل عملية أكثر أهمية بعد ذلك ، فسنناقش خوارزمية الجدولة لاحقًا.

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

    إشارة أخرى تدور حول التزامن. يتم استخدام الإشارات الكاملة والفارغة للتأكد من حدوث الحدث أو لا يحدث. في هذا المثال ، يضمنون أن المنتج يتوقف عن تشغيله عندما يكون المخزن المؤقت ممتلئًا ؛ يتوقف المخزن المؤقت عن تشغيله عندما يكون فارغًا. يختلف استخدام هاتين الإشارات اثنين عن Mutex.

    موتيكس

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

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

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

    من ناحية أخرى ، إذا كان Mutex قد تم قفله ، فسيقوم مؤشر ترابط الاتصال بحظر الخيوط في المنطقة الحرجة و Call Mutex_unlock. إذا تم حظر خيوط متعددة على الفصل المتبادل لـ Mutex ، فسيكون مؤشر ترابط عشوائيًا ويسمح له بالحصول على أقفال.

    نظرًا لأن الكمية المتبادلة لـ Mutex بسيطة للغاية ، طالما أن هناك تعليمات TSL أو XCHG ، فيمكنها تنفيذها بسهولة في مساحة المستخدم. رمز Mutex_lock و mutex_unlock لحزمة المستخدم -lovel هي كما يلي ، وجوهر XCHG هو نفسه.

    MUTEX_LOCK: سجل TSL ، MUTEX | انسخ الإقصاء المتبادل إلى السجل ، ووضع كمية الإشارة المتبادلة إلى سجل 1CMP ،#0 | إشارة الاستبعاد المتبادل 0؟ jze ok | إذا كانت رسالة الاستبعاد المتبادلة هي 0 ، فسيتم إلغاء قفلها ، لذا فإنها تُرجع call thread_yield | إشارة متبادلة قيد الاستخدام ؛ إرسال المواضيع الأخرى jmp mutex_lock | حاول مرة أخرى موافق: ret | مرة أخرى إلى المتصل ، أدخل المنطقة الحرجة Mutex ،#0 | ضع mutex إلى 0ret | العودة إلى المتصل

    رمز mutex_lock يشبه إلى حد كبير رمز enter_region أعلاه ، يمكننا مقارنته

    هل ترى الفرق الأكبر بين الكود أعلاه؟

    • استنادًا إلى تحليلنا لـ TSL أعلاه ، نعلم أنه إذا قضت TSL أن العملية التي لا تدخل المنطقة الحرجة ستؤدي دورة غير محدودة الحصول على أقفال ، وفي معالجة TSL ، إذا كانت Mutex تستخدم ، فسيتم إرسال مؤشرات الترابط الأخرى للمعالجة. لذلك فإن الفرق الأكبر أعلاه هو في الواقع المعالجة بعد الحكم على Mutex/TSL.
    • thread_yield CPU

    enter_region mutex_lock thread_yield mutex_lock mutex_unlock

    mutex_trylock

    Futexes

    (synchronization)(locking) (Spin lock) CPU Unfortunately, this method will also lead to another problem: it can run well during the frequent process competition, but the consumption of kernel switching is not very intense, and it is even more difficult to predict the lock lock. The number of competition is even أكثر صعوبة.

    futex (fast user space mutex)

    futex Linux futex جوهر (wait queue)

    futex 32 (integer) 1(decrement and test) decrement and set Linux C futex (increment and test)

    Pthreads

    Pthreads mutex mutex mutex mutex

    mutex Phread_mutex_init Pthread_mutex_destroymutex Pthread_mutex_lock Pthread_mutex_trylock mutex Pthread_mutex_unlock mutex

    Pthreads (condition variables) mutex

    mutex

    pthread

    Pthread_cond_wait Pthread_cond_signalPthread_cond_broadcast

    "" "

    #تضمن < stdio.h > #تضمن < pthread.h > #define MAX 1000000000/* */pthread_mutex_t the_mutex;pthread_cond_t condc,condp;/* */int buffer = 0;void *producer(void *ptr){/* */ int i; for(int i = 0;i < = MAX;i++){ pthread_mutex_lock(the_mutex);/* mutex */ while(buffer != 0){ pthread_cond_wait(condp,the_mutex); } buffer = i;/* */ pthread_cond_signal(condc);/* */ pthread_mutex_unlock(the_mutex); /* */ } pthread_exit(0); }void *consumer(void *ptr){/* */ int i; for(int i = 0;i < = MAX;i++){ pthread_mutex_lock(the_mutex);/* mutex */ while(buffer == 0){ pthread_cond_wait(condc,the_mutex); } buffer = 0;/* */ pthread_cond_signal(condp);/* */ pthread_mutex_unlock(the_mutex); /* */ } pthread_exit(0); }

    خط انابيب

    Brinch Hansen Hoare (monitor) Pascal C C

    monitor exampleinteger i;condition c;procedure producer();...end;procedure consumer();.end;end monitor;

    (mutex) (binary semaphore)

    -

    (condition variables) wait signal full wait pthread signal

    "" "

    Brinch Hansen Hoare Hoare Brinch Hansen signal Brinch Hansen

    signal

    signal

    هذا هو، wait signal جوهر

    Pascal -

    monitor ProducerConsumercondition full,empty;integer count;procedure insert(item:integer);beginif count = N then wait(full);insert_item(item);count := count + 1;if count = 1 then signal(empty);end;function remove:integer;beginif count = 0 then wait(empty);remove = remove_item;count := count - 1;if count = N - 1 then signal(full);end;count := 0;end monitor;procedure producer;beginwhile true do begin item = produce_item; ProducerConsumer.insert(item); endend;procedure consumer;beginwhile true dobeginitem = ProducerConsumer.remove;consume_item(item);endend;

    wait signal sleep wakeup sleep wakeup ليست هذه هي القضية. The automatic mutual exclusion of the pipe program ensures this. If the producers during the process of the process found that the buffer is full, it will be able to complete the WAIT operation without worrying that the scheduling program may switch to consumers before the Wait is منجز. wait

    Pascal Java Java Java synchronized Java synchronized synchronized

    هنا

    public class ProducerConsumer { static final int N = 100;// static Producer p = new Producer();// static Consumer c = new Consumer(); // static Our_monitor mon = new Our_monitor(); // static class Producer extends Thread{ public void run(){// run int item; while(true){// item = produce_item(); mon.insert(item); } } private int produce_item(){...}// } static class consumer extends Thread { public void run( ) {// run int item; while(true){ item = mon.remove();consume_item(item); } } private int produce_item(){...}// } static class Our_monitor {// private int buffer = new int; private int count = 0,lo = 0,hi = 0; // private synchronized void insert(int val){ if(count == N){ go_to_sleep();// }buffer = val;// hi = (hi + 1) % N; // count = count + 1;// 1 if(count == 1){ notify();// } } private synchronized void remove(int val){ int val; if(count == 0){ go_to_sleep();// } val = buffer;// lo = (lo + 1) % N;// count = count - 1;// 1 if(count = N - 1){ notify();// } return val; } private void go_to_sleep() { try{ wait( ); }catch(Interr uptedExceptionexc) {}; } } }

    (outer class) ProducerConsumer p c Producer Consumer Our_monitor

    Our_monitor insert remove count lo hi lo = hi 0 N

    Java Java Java wait notify sleep wakeup

    جوهر CPascal

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

    نقل الرسالة

    الطرق المذكورة أعلاه المذكورة أعلاه هي Messaage تمرير. تستخدم طريقة الاتصال هذه بين العمليات اثنين من الإرسال والاستقبال البدائي. وهما مثل الإشارة بدلاً من خطوط الأنابيب ، والتي هي مكالمات النظام بدلاً من مستويات اللغة. المثال كما يلي

    إرسال (وجهة ، رسالة) ؛ استلام (المصدر ، الرسالة) ؛

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

    نقاط تصميم نظام نقل الرسائل

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

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

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

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

    استخدم الرسائل لنقلها لحل مشكلة المنتج المستهلك

    الآن نحن نفكر في كيفية استخدام الرسائل اللازمة لحل مشكلة المنتج المستهلك ، وليس مشاركة ذاكرة التخزين المؤقت. ما يلي حل

    #define n 100/*buffer عدد فتحات*/void producer (void) {int item ؛ message m ؛/*number buffer*/about (true) {item = signem () ؛ Data of the Buffer*/rece ، M) ؛/*انتظر حتى يرسل المستهلكون مخزن مؤقت فارغ*/build_message (م ، عنصر) ؛/*إنشاء رسالة ليتم إرسالها*/إرسال (المستهلكين ، M) ؛/*إرسال إلى المستهلكين*/}} void المستهلك (void) {int item ، i ؛ message m ؛ for (int i = 0 ؛ i < n ؛ i ++) {/* cycle n times*/send (المنتج ، m) ؛/* إرسال n buffer*/} بينما (صحيح) {استلام (منتج ، m) ؛/* قبول رسالة البيانات المدرجة*/ العنصر = extract_item (m) ؛/* استخراج البيانات من الرسالة*/send (المنتج ، m) ؛/* أرسل المخزن المؤقت للهواء إلى المنتج*/strupe_item (IT) ؛/* Process Data*/}}}

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

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

    هناك العديد من المتغيرات في طريقة نقل الرسائل. يقدم التالية كيفية كتابة الرسالة.

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

    حاجز

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

    في الشكل أعلاه ، يمكننا أن نرى أن أربع عمليات قريبة من الحاجز ، مما يعني أن كل عملية تؤدي التشغيل ، لكنها لم تصل إلى نهاية كل مرحلة. بعد فترة من الزمن ، وصلت العمليات الثلاث لـ A و B و D إلى الحاجز ، وتم تعليق عملياتها الخاصة ، لكن في هذا الوقت ، لم يتمكنوا من الدخول إلى المرحلة التالية ، لأن العملية B لم يتم تنفيذها بعد . نتيجة لذلك ، عندما وصل آخر C إلى الحاجز ، يمكن لمجموعة العملية أن تدخل المرحلة التالية.

    تجنب القفل: قراءة النسخ

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

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

    في الشجرة أعلاه ، تعبر عملية القراءة الشجرة بأكملها من الجذر إلى الأوراق. بعد إضافة عقدة جديدة X ، من أجل تحقيق هذه العملية ، يجب أن ندع هذه العقدة تُرى قبل الشجرة "Just Right": نهيئة جميع القيم في العقدة X ، بما في ذلك مؤشرها الفرعي. ثم اكتب العملية من خلال الذرة ، بحيث يطلق على x sub -node of A. لن تتم قراءة جميع عمليات القراءة غير متسقة قبل وبعد

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

    الاختيار المبكر

    عشرة آلاف حرف يأخذك لاستعادة العملية والخيط

    مفهوم أنظمة التشغيل هذه لم يسمع عنك أبدًا!

    ما هو نظام التشغيل | الميل التكتيكي

    هل تريد أن تسألني طبقة التطبيق؟ أنا فقط أسحبك معك

    مرجع المادة:

    "نظام التشغيل الحديث"

    طبعة "نظام التشغيل الحديث"

    https://www.encyclopedia.com/computing/news-wires-papers-nd-books/interactive-systems

    https://j00ru.vexillium.org/syscalls/nt/32/

    https://www.bottomupcs.com/process_hierarchy.xhtml

    https://en.wikipedia.org/wiki/runtime_system

    https://en.wikipedia.org/wiki/execution_model

    طي، بيثون للعب موتاهم بك! العضو: لا شفقة

    نتحدث عن برنامج تخطيط الذاكرة

    كيفية دمج وتخفيض مع ASP.NET الأساسية؟

    بايدو أغلقت منصة متنقلة قنوات الروبوت، أبل سوف يدفع اي فون SE 2 الأجيال، ومايكروسوفت مفتوحة المصدر عددي | المهوسون العناوين

    50000 شخص يعملون من المنزل، والتنسيق مدى فعالية؟ البايت يوفر دليلا للضرب

    آلة التعلم تحتل قائمة جيدة الأجر، سلسلة كتلة هدأت؟ مهندس برمجيات وضع تفسير 2020

    الماضي والحاضر في تطوير شرائح الذكاء الاصطناعي

    حول "جرائم" بيثون ثلاثة! لماذا برمجة قدرة 10 مرة أفضل مما كنت الذين مشيدا ذلك؟

    العمل من المنزل، وأنا أذهب إلى العمل متعب أيضا من كنت تعتقد ذلك؟

    وزارة الدعاية التعليم 200 الحادي عشر مفتشي الدولة، و 300 خاص المشرف التعليم قائمة Nipin

    أهنئ! 7 حصل خريجو جامعة بكين على جائزة سلون للأبحاث

    بطاريات الحالة الصلبة، والطب عمياء لتعزيز صناعة الطاقة الجديدة