الدرس 18: الإدخال/الإخراج غير الحاجب (Non-blocking I/O) و epoll
الإدخال/الإخراج الحاجب هو الوضع الافتراضي — إذ ينتظر read() حتى تصل البيانات. مع O_NONBLOCK، يعود read() فورًا مع EAGAIN إذا لم تكن هناك بيانات جاهزة. لكن كيف تدير آلاف الـ FDs في وقت واحد؟ يحل epoll هذا بالضبط — حلقة أحداث تُعلمنا فقط عندما يصبح FD جاهزًا، بتعقيد O(1). يستخدم خادم Triton Inference
الإدخال/الإخراج الحاجب أشبه بالوقوف في طابور لشراء تذكرة والانتظار هناك. أما غير الحاجب فهو أن تقول للموظف 'اتصل بي عندما تكون جاهزًا'. و epoll أشبه بعامل مقسم هاتفي يدير آلاف المكالمات، ولا يمرر إليك سوى تلك التي تنتظر ردك.
- الإدخال/الإخراج غير الحاجب (non-blocking I/O)
- وضع لا تحجب فيه استدعاءات I/O (read، write، accept). إذا لم تكن هناك بيانات جاهزة، تعود بالقيمة -1 مع errno=EAGAIN/EWOULDBLOCK. يُفعَّل باستخدام الراية O_NONBLOCK.
- epoll
- واجهة Linux لمراقبة أحداث I/O على مجموعة كبيرة من واصفات الملفات (file descriptors). تتألف من epoll_create و epoll_ctl و epoll_wait. تعيد فقط الـ FDs الجاهزة — بتعقيد O(1) مقابل O(n) لدالة select.
- المُشغَّل بالحافة (edge-triggered, ET)
- وضع epoll يُبلَّغ فيه عن الحدث مرة واحدة فقط — عندما ينتقل الـ FD من غير جاهز إلى جاهز. يتطلب القراءة حتى EAGAIN في كل مرة. أكثر كفاءة لكنه أكثر تعقيدًا.
- المُشغَّل بالمستوى (level-triggered, LT)
- وضع epoll الافتراضي — يُبلَّغ عن الحدث في كل مرة يُستدعى فيها epoll_wait ما دام الـ FD لا يزال جاهزًا. أبسط من ET لكنه قد يسبب حلقة انشغال (busy-loop) إذا لم يُعالَج بشكل صحيح.
- حلقة الأحداث (event loop)
- نمط برمجي ينتظر فيه البرنامج الأحداث ويعالجها واحدًا تلو الآخر — حلقة لا نهائية تستدعي epoll_wait وتُطلق دوال رد النداء (callbacks). وهو نواة Node.js و Nginx و Triton.