الحقيقة الكاملة عن لينكس إبول

حسنًا ، أو تقريبًا كل شيء ...



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


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


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

أفترض أن القارئ على دراية بـ epoll ، على الأقل اقرأ صفحة الدليل. لقد كتب ما يكفي عن epoll ، استطلاع ، حدد بحيث سمع كل من يتطور في Linux عن ذلك مرة واحدة على الأقل.


الكثير من قوات الدفاع عن الديمقراطية


عندما يتحدث الناس عن epoll بشكل أساسي ، أسمع فرضية مفادها أن "أدائه أفضل عندما يكون هناك الكثير من واصفات الملفات".


فقط أريد طرح سؤال - كم هو كم؟ كم عدد الاتصالات المطلوبة ، والأهم من ذلك ، تحت أي ظروف ستبدأ epoll في إعطاء مكاسب أداء ملموسة؟


بالنسبة لأولئك الذين درسوا epoll (هناك الكثير من المواد بما في ذلك المقالات العلمية) ، فإن الإجابة واضحة - من الأفضل إذا وفوق عدد مركبات "انتظار حدث" بشكل كبير عدد "جاهز للمعالجة". علامة الكمية ، عندما يصبح الربح كبيرًا لدرجة أنه لا يوجد ببساطة بول لتجاهل هذه الحقيقة ، يتم اعتبار مركبات 10k [4].


يأتي افتراض أن معظم الاتصالات معلقة من منطق الصوت ومراقبة تحميل الخوادم قيد الاستخدام النشط.


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


من الواضح ، في الحالة الأخيرة ، نقضي وقتًا في اجتياز جميع الواصفات + النفقات العامة لنسخ مجموعة من الأحداث من النواة.


في الواقع ، في قياس الأداء الأولي ، الذي تم إرفاقه بالرقعة [9] ، لم يتم التأكيد على هذه النقطة ويمكن للمرء أن يخمن فقط من خلال وجود أداة deadcon المساعدة المذكورة في المقالة (للأسف ، يتم فقدان رمز الأداة المساعدة pipetest.c). من ناحية أخرى ، في مصادر أخرى [6 ، 8] من الصعب جدًا عدم ملاحظة ذلك ، لأن هذه الحقيقة تبرز عمليا.


يطرح السؤال على الفور ، ولكن ماذا الآن إذا لم يكن من المخطط لخدمة هذا العدد من واصفات ملف epoll ، كما كانت ، وليس هناك حاجة؟


على الرغم من حقيقة أن epoll تم إنشاؤه خصيصًا لمثل هذه الحالات [5 ، 8 ، 9] ، هذا بعيد عن الاختلاف الوحيد بين epoll .


النشرة


بادئ ذي بدء ، سنلقي نظرة على الفرق بين مشغلات الحافة المشغلة والمستويات المشغلة للمستوى. هناك بيان جيد جدًا حول هذا الموضوع في المقالة المقاطعات المشغلة Edge Triggered Vs Level Triggered - Venkatesh Yadav :


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

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

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


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


مثال محدد مع epoll:


تم تشغيل المستوى


  • تمت إضافة المقبض إلى epoll بعلامة EPOLLIN
  • كتل epoll_wait () أثناء انتظار الحدث
  • الكتابة إلى واصف الملف 19 بايت
  • يفتح epoll_wait () حدث EPOLLIN
  • نحن لا نفعل شيئا بالبيانات التي جاءت
  • يفتح epoll_wait () مرة أخرى مع حدث EPOLLIN

وسيستمر هذا حتى نحسب أو نعيد تعيين البيانات من الواصف.


أثار الحافة


  • تمت إضافة المقبض إلى epoll مع أعلام EPOLLIN | النشرة
  • كتل epoll_wait () أثناء انتظار الحدث
  • الكتابة إلى واصف الملف 19 بايت
  • يفتح epoll_wait () حدث EPOLLIN
  • نحن لا نفعل شيئا بالبيانات التي جاءت
  • تم حظر epoll_wait () في انتظار حدث جديد
  • اكتب 19 بايت أخرى إلى واصف الملف
  • يفتح epoll_wait () حدث EPOLLIN الجديد
  • تم حظر epoll_wait () في انتظار حدث جديد

مثال بسيط: epollet_socket.c


تم تصميم هذه الآلية لمنع عودة epoll_wait () بسبب حدث قيد المعالجة بالفعل.


إذا ، في حالة المستوى ، عند استدعاء epoll_wait () ، تتحقق النواة لمعرفة ما إذا كانت fd في هذه الحالة ، ثم تتخطى الحافة هذا الاختيار وتضع عملية الاتصال على الفور في حالة السكون.


EPOLLET نفسها هي ما يجعل epoll O (1) معددًا للأحداث.


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


// TCP socket (byte stream) //  fd    EPOLLIN      int len = read(fd, buffer, BUFFER_LEN); if(len < BUFFER_LEN) { //   } else { //         //  -       epoll_wait, //      } 

  // accept //  listenfd    EPOLLIN      event.events = EPOLLIN | EPOLLERR; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event); sleep(5); //       >1  //   while(epoll_wait()) { newfd = accept(listenfd, ...); //      //        //  epoll_wait    listenfd    } //   while(epoll_wait()) { while((newfd = accept(...)) > 0) { //  -  } if(newfd == -1 && errno = EAGAIN) { //       //       } } 

مع هذه الخاصية ، يكفي المجاعة:


  • تأتي الحزم إلى الواصف
  • قراءة الحزم في المخزن المؤقت
  • تأتي حزمة أخرى
  • قراءة الحزم في المخزن المؤقت
  • يأتي جزء صغير
  • ...

وبالتالي ، لن نتلقى EAGAIN قريبًا ، ولكن قد لا نستقبله على الإطلاق.


وبالتالي ، لا تتلقى واصفات الملفات الأخرى وقتًا للمعالجة ، ونحن مشغولون بقراءة أجزاء صغيرة من البيانات تصل باستمرار.


الرعد الطالب الذي يذاكر كثيرا قطيع


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


الرعد مشكلة القطيع


مشكلة الرعد القطيع

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

مصطلحات تكنولوجيا المعلومات - فاسيلي ألكسينكو

في هذه الحالة ، نحن مهتمون بمشكلة قبول () وقراءة () الموزعة عبر التدفقات بالاقتران مع epoll .


قبول


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


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


بالطبع ، ستتمكن () من القيام بخيوط واحدة فقط ، وسيتلقى الباقي EAGAIN ، ولكن هذا مضيعة للموارد.


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


وهذا يقودنا مرة أخرى إلى وضع تم فيه إيقاظ التيار المجاور دون جدوى.


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


كرة القدم


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


باختصار - إذا تم استخدام EPOLLONESHOT ، فسيتم إطلاق الحدث المرتبط بواصف معين مرة واحدة فقط ، وبعد ذلك من الضروري إعادة ترقيم الأعلام باستخدام epoll_ctl () .


EPOLLEXCLUSIVE


هنا يأتي EPOLLEXCLUSIVE والمحفز على المستوى لمساعدتنا.


يفتح EPOLLEXCLUSIVE أداة epoll_wait () واحدة معلقة في وقت واحد لحدث واحد.


المخطط بسيط للغاية (في الواقع ليس):


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

وبالتالي ، يتم توزيع جميع عمليات الصيانة بالتساوي عبر التدفقات.


 $ ./epollexclusive --help -i, --ip=ADDR specify ip address -p, --port=PORT specify port -n, --threads=NUM specify number of threads to use #    -  n*8 -t, --thunder not adding EPOLLEXCLUSIVE #     thunder herd -h, --help prints this message $ sudo taskset -c 0-7 ./epollexclusive -i 10.56.75.201 -p 40000 -n 8 2>&1 

رمز المثال: epollexclusive.c (سيعمل فقط مع إصدار kernel من 4.5)


نحصل على نموذج ما قبل الشوكة على epoll. هذا المخطط قابل للتطبيق بشكل جيد لاتصالات TCP قصيرة الوقت .


اقرأ


ولكن مع قراءة () في حالة تدفق البايت ، لن تساعدنا EPOLLEXCLUSIVE ، مثل EPOLLET .


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


مع EPOLLET ، فإن الوضع هو نفسه.


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


  • تمت إضافة المقبض إلى epoll مع أعلام EPOLLONESHOT | النشرة
  • الانتظار على epoll_wait ()
  • قراءة من مأخذ التوصيل إلى المخزن المؤقت حتى ترجع read () EAGAIN
  • إعادة تهيئة مع أعلام EPOLLONESHOT | النشرة

هيكل epoll_event


 typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; 

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


 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct epoll_client { /** some usefull associated data...*/ struct epoll_event event; }; struct epoll_client* to_epoll_client(struct epoll_event* event) { return container_of(event, struct epoll_client, event); } struct epoll_client ec; ... epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ec.e); ... epoll_wait (efd, events, 1, -1); struct epoll_client* ec_ = to_epoll_client(events[0].data.ptr); 

أعتقد أن الجميع يعرف من أين أتت هذه التقنية.


الخلاصة


آمل أن نتمكن من فتح موضوع epoll . أولئك الذين يريدون استخدام هذه الآلية بوعي ، يحتاجون فقط إلى قراءة المقالات في قائمة المراجع [1 ، 2 ، 3 ، 5].


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


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


التفكير في "خصوصية" المشكلة


قبل أن يتحدث شخص ما عن خصوصية هذه العلامات وأنماط الاستخدام ، أريد أن أطرح سؤالًا:


"ولكن أليس شيئًا ما نحاول مناقشة خصوصية الآلية التي تم إنشاؤها لمهام محددة في البداية [9 ، 11]؟ أم أننا حتى نخدم اتصالات 1k هي مهمة يومية للمبرمج؟"


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


للمشككين ، زوجان من الروابط:


زيادة الأداء باستخدام SO_REUSEPORT في NGINX 1.9.1 - VBart
التعلم من يونيكورن: القبول () الرعد القطيع بدون مشكلة - كريس سيبنمان
تسلسل قبول () ، AKA Thundering Herd ، AKA the Zeeg Problem - روبرتو دي إيوريس
كيف يتفاعل وضع EPOLLEXCLUSIVE epoll مع تشغيل المستوى؟


المراجع


  1. اختر مكسورة بشكل أساسي - ماريك
  2. كسر Epoll بشكل أساسي 1/2 - ماريك
  3. كسر Epoll بشكل أساسي 2/2 - ماريك
  4. مشكلة C10K - دان كيجل
  5. استطلاع رأي Epoll ، مرة أخرى - جاك Mattheij
  6. epoll - مرفق إخطار حدث I / O - The Mann
  7. طريقة جنون Epoll - Cindy Sridharan

المعايير


  1. https://www.kernel.org/doc/ols/2004/ols2004v1-pages-215-226.pdf
  2. http://lse.sourceforge.net/epoll/index.html
  3. https://mvitolin.wordpress.com/2015/12/05/endurox-testing-epollexclusive-flag/

تطور epoll


  1. https://lwn.net/Articles/13918/
  2. https://lwn.net/Articles/520012/
  3. https://lwn.net/Articles/520198/
  4. https://lwn.net/Articles/542629/
  5. https://lwn.net/Articles/633422/
  6. https://lwn.net/Articles/637435/

بوستسكريبت


شكرا جزيلا لسيرجي ( dlinyj ) وبيتر أوفشينكوف على المناقشات القيمة والتعليقات والمساعدة!

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


All Articles