epoll. سنتحدث هنا عن كيفية epollنقل الأحداث من مساحة kernel إلى مساحة المستخدم ، وكيفية تنفيذ أوضاع تشغيل الحافة والمستوى.
تمت كتابة هذه المقالة في وقت متأخر عن غيرها. عندما بدأت العمل على المادة الأولى ، كانت أحدث نواة Linux مستقرة 3.16.1. وفي وقت كتابة هذا التقرير ، كان هذا بالفعل الإصدار 4.1. تستند هذه المقالة إلى رمز إصدار kernel هذا. ومع ذلك ، لم يتغير الكود كثيرًا ، لذا يجب ألا يقلق قراء المقالات السابقة من أن شيئًا ما في التنفيذ قد تغير كثيرًا.
epoll
التفاعل مع مساحة المستخدم
في المقالات السابقة ، قضيت الكثير من الوقت في شرح كيفية عمل نظام معالجة الأحداث في النواة. ولكن ، كما تعلم ، يحتاج kernel إلى تمرير معلومات حول الأحداث إلى برنامج يعمل في مساحة المستخدم حتى يتمكن البرنامج من استخدام هذه المعلومات. يتم ذلك بشكل أساسي مع استدعاء النظام epoll_wait (2) .
يمكن العثور على رمز هذه الوظيفة في السطر 1961 من الملف
fs/eventpoll.c. الوظيفة نفسها بسيطة للغاية. بعد إجراء فحوصات عادية تمامًا ، يحصل ببساطة على المؤشر eventpollمن واصف الملف ويستدعي الوظيفة التالية:
error = ep_poll(ep, events, maxevents, timeout);
وظيفة Ep_poll ()
تم
ep_poll()التصريح عن الوظيفة في السطر 1585 من نفس الملف. يبدأ بالتحقق لمعرفة ما إذا كان المستخدم قد حدد قيمة timeout. إذا كان الأمر كذلك ، تقوم الوظيفة بتهيئة قائمة انتظار الانتظار وتعيين المهلة على القيمة المحددة من قبل المستخدم. إذا كان المستخدم لا يريد الانتظار ، , timeout = 0فحينئذٍ تنتقل الوظيفة فورًا إلى كتلة التعليمات البرمجية ذات التسمية check_events:، وهي المسؤولة عن نسخ الحدث.
إذا حدد المستخدم قيمة
timeout، ولا توجد أحداث يمكن الإبلاغ عنها (يتم تحديد وجودها باستخدام مكالمة ep_events_available(ep)) ، ep_poll()تضيف الوظيفة نفسها إلى قائمة الانتظار ep->wq(تذكر ما تحدثنا عنه في المقالة الثالثة من هذه السلسلة). هناك ذكرنا أنه ep_poll_callback()في هذه العملية ، ينشط أي عمليات تنتظر في قائمة الانتظار.ep->wq...
تنتقل الوظيفة بعد ذلك إلى وضع الاستعداد عن طريق الاتصال
schedule_hrtimeout_range(). فيما يلي الظروف التي يمكن فيها لعملية "نائمة" أن "تستيقظ":
- انتهت المهلة.
- تلقت العملية إشارة.
- نشأ حدث جديد.
- لم يحدث شيء ، وقرر المجدول فقط تنشيط العملية.
في السيناريوهات 1 و 2 و 3 ، تقوم الوظيفة بتعيين العلامات المناسبة وتخرج من حلقة الانتظار. في الحالة الأخيرة ، تعود الوظيفة ببساطة إلى وضع الاستعداد.
بعد الانتهاء من هذا الجزء من العمل ،
ep_poll()يستمر تنفيذ كود الحظر check_events:.
في هذه الكتلة ، يتم التحقق أولاً من وجود الأحداث ، ثم يتم إجراء المكالمة التالية ، حيث يحدث الأكثر إثارة للاهتمام.
ep_send_events(ep, events, maxevents)
تم
ep_send_events()الإعلان عن الوظيفة في السطر 1546. وهي ، بعد الاستدعاء ، تستدعي الوظيفة ep_scan_ready_list()، وتمرير رد الاتصال ، ep_send_events_proc(). ep_scan_ready_list()تتكرر الوظيفة عبر قائمة واصفات الملفات الجاهزة وتستدعي ep_send_events_proc()كل حدث جاهز تجده. سيتضح أدناه أن هناك حاجة إلى آلية تتضمن استخدام رد الاتصال لضمان إعادة استخدام الأمان والتعليمات البرمجية.
تقوم الوظيفة
ep_send_events()أولاً بوضع البيانات من قائمة واصفات الملفات الجاهزة للهيكل eventpoolفي المتغير المحلي الخاص بها. ثم يقوم بتعيين حقل ovflistالهيكل eventpoolإلى NULL(والافتراضي هو EP_UNACTIVE_PTR).
لماذا
epollيستخدم المؤلفونovflist؟ يتم ذلك لضمان كفاءة عالية epoll! قد تلاحظ أنه بعد أخذ قائمة واصفات الملفات الجاهزة من الهيكل eventpool، يتم ep_scan_ready_list()ضبطها ovflistعلى NULL. ينتج عن هذا ep_poll_callback()عدم محاولة إرفاق حدث تم تمريره إلى مساحة المستخدم مرة أخرى ep->rdllist، مما قد يؤدي إلى مشاكل كبيرة. باستخدام ovflistالوظيفة ، ليست ep_scan_ready_list()هناك حاجة للاحتفاظ بالقفل ep->lockأثناء نسخ الأحداث إلى مساحة المستخدم. نتيجة لذلك ، تم تحسين الأداء العام للحل.
بعد ذلك ،
ep_send_events_proc()سيتخطى قائمة واصفات الملفات الجاهزة لديه ويستدعي أساليبهم مرة أخرى.poll()للتأكد من أن الحدث قد حدث بالفعل. لماذا epollتحقق من الأحداث هنا مرة أخرى؟ يتم ذلك للتأكد من أن الحدث (أو الأحداث) التي سجلها المستخدم لا تزال متاحة. ضع في اعتبارك الموقف الذي تمت فيه إضافة واصف ملف إلى قائمة واصف الملف الجاهز حسب الحدث EPOLLOUTأثناء قيام برنامج المستخدم بالكتابة إلى هذا الواصف. بعد انتهاء البرنامج من الكتابة ، قد لا يكون واصف الملف قابلاً للكتابة. Epollتحتاج إلى التعامل مع مثل هذه المواقف بشكل صحيح. خلاف ذلك ، سيتلقى المستخدم EPOLLOUTفي الوقت الذي يتم فيه حظر عملية الكتابة.
ومع ذلك ، تجدر الإشارة هنا إلى تفصيل واحد. وظيفة
ep_send_events_proc()يبذل قصارى جهده لضمان تلقي برامج مساحة المستخدم إعلامات دقيقة عن الأحداث. من الممكن ، على الرغم من أنه من غير المحتمل ، أن يتغير توافر مجموعة من الأحداث بعد ep_send_events_proc()بدء التشغيل poll(). في هذه الحالة ، قد يتم إخطار برنامج فضائي للمستخدم بحدث لم يعد موجودًا. هذا هو السبب في أنه من الصحيح دائمًا استخدام مآخذ غير معطلة عند تطبيقها epoll. هذا يمنع التطبيق الخاص بك من أن يتم حظره بشكل غير متوقع.
بعد التحقق من قناع الحدث ، يقوم
ep_send_events_proc()ببساطة بنسخ بنية الحدث إلى المخزن المؤقت الذي يوفره برنامج مساحة المستخدم.
الحافة المشغلة والمستوى
الآن يمكننا أخيرًا مناقشة الاختلاف بين Edge Triggering (ET) و Level Triggering (LT) من حيث تنفيذها.
else if (!(epi->event.events & EPOLLET)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
}
انه سهل جدا!
ep_send_events_proc()تضيف الوظيفة الحدث مرة أخرى إلى قائمة واصفات الملفات الجاهزة. نتيجة لذلك ، في المكالمة التالية ، ep_poll()سيتم فحص نفس واصف الملف مرة أخرى. نظرًا لأنه ep_send_events_proc()يستدعي دائمًا ملفًا poll()قبل إعادته إلى تطبيق مساحة المستخدم ، فإن هذا يزيد من تحميل النظام قليلاً (مقارنةً بـ ET) إذا لم يعد واصف الملف متاحًا. لكن الهدف من كل هذا ، كما ذكر أعلاه ، هو عدم الإبلاغ عن الأحداث التي لم تعد متاحة.
بعد أن
ep_send_events_proc()تنتهي من نسخ الأحداث ، تقوم الوظيفة بإرجاع عدد الأحداث المنسوخة إليها ، مما يحافظ على تحديث تطبيق مساحة المستخدم.
عندما
ep_send_events_proc()تنتهي الوظيفة ، فإن الوظائفep_scan_ready_list()بحاجة إلى تنظيف قليلا. أولاً ، يعود إلى قائمة واصفات الملفات الجاهزة للأحداث التي تركتها الوظيفة دون معالجة ep_send_events_proc(). يمكن أن يحدث هذا إذا تجاوز عدد الأحداث المتاحة حجم المخزن المؤقت الذي يوفره برنامج المستخدم. كما يقوم أيضًا ep_send_events_proc()بإرفاق جميع الأحداث سريعًا من ovflistقائمة واصفات الملفات الجاهزة ، إن وجدت. علاوة على ذلك ، يتم ovflistتسجيله مرة أخرى EP_UNACTIVE_PTR. نتيجة لذلك ، سيتم إرفاق الأحداث الجديدة بقائمة الانتظار الرئيسية ( rdllist). يتم إنهاء الوظيفة عن طريق تنشيط أي عمليات "سكون" أخرى في حالة وجود أي أحداث أخرى متاحة.
النتيجة
هذا يختتم المقال الرابع والأخير في سلسلة التنفيذ
epoll. أثناء كتابة هذه المقالات ، تأثرت بالعمل الذهني الهائل الذي قام به مؤلفو كود نواة Linux لتحقيق أقصى قدر من الكفاءة وقابلية التوسع. وأنا ممتن لجميع مؤلفي كود Linux لمشاركة معرفتهم مع كل من يحتاجها من خلال مشاركة نتائج عملهم.
ما هو شعورك حيال البرمجيات مفتوحة المصدر؟
