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

على مدى أكثر من عقدين، اعتمدت نواة لينكس على خوارزمية LRU التقليدية ثنائية القوائم (Active/Inactive) لإدارة إخلاء الصفحات. ورغم ملاءمة هذا التصميم للخوادم العامة، فإنه يتسبب في طفرات كارثية في زمن الاستجابة الطرفي (tail latency) على بوابات الشبكة الحساسة للوقت. فعندما تنخفض الذاكرة الحرة تحت الحد الأدنى، تدخل النواة في مرحلة الاستعادة المباشرة (direct reclamation): حيث تتعطل مسارات معالجة الحزم لعشرات أو مئات الأجزاء من الثانية لمسح سلاسل التعيين العكسي، مما يؤدي إلى فقدان الحزم وتدهور جلسات الوسائط الحية.

لقد غيرت بنية Multi-Gen LRU (التي تم دمجها في نواة لينكس 6.1) بالتكامل مع نظام Pressure Stall Information (المقدم في لينكس 4.20) مفاهيم إدارة الذاكرة في شبكات الحافة جذريا. يوفر هذا المزيج تصنيفا جليا لصفحات الذاكرة، وفحصا لجداول الصفحات بدون أقفال، ومراقبة للاختناق على مستوى الميكروثانية، مما يسمح لموجهات الشبكة بالحفاظ على تدفق البيانات دون تراجع مفاجئ في الأداء.

اختناقات خوارزمية LRU التقليدية ثنائية القوائم

لفهم أسباب تراجع الأداء في بوابات الشبكات، يتعين تفحص آلية عمل النظام ثنائي القوائم الكلاسيكي:

بنية LRU ثنائية القوائم التقليدية:
[ القائمة النشطة (صفحات ساخنة) ] <--- تنافس شديد على القفل العام (zone->lru_lock) ---> [ القائمة الخاملة (صفحات باردة) ]
               |                                                                                     |
          مسح kswapd                                                                            الإخلاء / التبديل
      (فحص rmap O(N)، استهلاك CPU)                                                          (توقف مباشر للمسارات)

ينطوي هذا التقسيم الثنائي على أربعة تحديات جوهرية في بوابات نقل الحزم:

  1. التنافس الشديد على القفل العام: تتطلب أي عملية إضافة أو ترقية أو إخلاء لصفحة ذاكرة الاستحواذ على القفل العام zone->lru_lock. وعندما تقوم عشرات الأنوية بمعالجة الحزم بالتوازي، تهدر خيوط المعالجة دورات ثمينة في انتظار تحرير القفل.
  2. عبء مسح التعيين العكسي (rmap): لتحديد ما إذا كانت صفحة في القائمة الخاملة قد تمت قراءتها مؤخرا، تضطر النواة إلى تتبع سلاسل المؤشرات العكسية عبر كافة مساحات العناوين للعمليات المرتبطة بها، مما يسبب إهدارا في الذاكرة المخبأة لوحدة المعالجة وزيادة في استهلاك الطاقة الحسابية.
  3. تصنيف زمني بدائي: تصنف الصفحة إما كنشطة أو خاملة، وتفتقر النواة إلى مقياس زمني يميز بين مخزن مؤقت تم استخدامه قبل ثانيتين وآخر لم يتم لمسه منذ عشر دقائق. ونتيجة لذلك، يتم إخلاء جداول التوجيه النشطة مبكرا دون مبرر.
  4. فقدان الحزم بسبب الاستعادة المباشرة: إذا عجزت عملية الخلفية kswapd عن توفير الذاكرة أثناء اندفاعات البيانات السريعة، تضطر مسارات الشبكة إلى تولي الاستعادة المباشرة ذاتيا. ويتوقف مسار المعالجة فجأة لانتظار عمليات القرص، مما يؤدي إلى فيضان طوابير الإدخال وإسقاط الحزم.

بنية Multi-Gen LRU: التقسيم الجيلي والمسح بدون أقفال

تستبدل بنية Multi-Gen LRU التقسيم الثنائي البسيط بمسار جيلي منظم يتم فيه تصنيف الصفحات ضمن أجيال متعددة ذات ترقيم تصاعدي:

مسار الأجيال في MGLRU:
[ الجيل N (الأحدث) ] ---> [ الجيل N-1 ] ---> [ الجيل N-2 ] ---> [ الجيل N-3 (الأقدم) ]
         ^                                                               |
         |------------- ترقية الصفحة عند الاستخدام ----------------------|
                                                                         v
                                                                   الإخلاء المنظم
                                                               (دون تجميد الحزم)

ترقية الأجيال وفحص جداول الصفحات بدون أقفال

بدلا من الاعتماد على فحص التعيين العكسي المعقد، تستخدم MGLRU ماسح جداول الصفحات (page-table walker) الذي يقرأ بتات الوصول العتادية مباشرة من مدخلات جدول الصفحات (PTE). وتتم هذه العملية بدون أقفال عامة اعتمادا على آلية RCU:

  • ترقية الأجيال: عند وصول حزمة مشفرة ومراجعة بياناتها، يتم تفعيل بت الوصول عتاديا. وخلال دورات الفحص المنتظمة، ترتقي الصفحات الساخنة إلى أحدث جيل (max_seq)، بينما تتراجع الصفحات الخاملة نحو الأجيال الأقدم (min_seq).
  • الإخلاء الدقيق للصفحات الخاملة: يتم قصر استعادة الذاكرة على صفحات الجيل الأقدم فقط (min_seq). وبما أن الفارق بين الجيل الأحدث والأقدم يعكس المدة الحقيقية لعدم الاستخدام، تنجح خوارزمية MGLRU في حماية مخازن الشبكة النشطة من التدفقات المؤقتة.
  • توازن الذاكرة المخبأة: تحافظ الخوارزمية على التوازن بين ذاكرة التخزين المؤقت للملفات والذاكرة المجهولة دون الاعتماد على تقديرات swappiness الاعتباطية، مما يحمي الذاكرة المخصصة لخدمات التشفير.

المراقبة المبكرة عبر Pressure Stall Information (PSI)

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

يقيس نظام PSI النسبة المئوية الدقيقة للوقت الذي تقضيه المهام في حالة توقف نتيجة لانتظار موارد الذاكرة، من خلال مقياسين رئيسيين:

/proc/pressure/memory:
some avg10 = 0.05 avg60 = 0.02 avg300 = 0.00 total = 142083
full avg10 = 0.00 avg60 = 0.00 avg300 = 0.00 total = 0
  • مؤشر some: نسبة الوقت التي يتعطل فيها مسار معالجة واحد على الأقل بانتظار الذاكرة، بينما تواصل المسارات الأخرى عملها.
  • مؤشر full: نسبة الوقت التي تتعطل فيها كافة المسارات الجاهزة على نواة المعالج بسبب نقص الذاكرة. وفي بوابات الشبكة، تدل أي قيمة تزيد عن الصفر لمؤشر full على خطر وشيك لفقدان الحزم.

تسمح نواة لينكس للتطبيقات بتسجيل محفزات عتبة عبر epoll(2) أو select(2) مباشرة على ملف /proc/pressure/memory. فإذا تجاوز التعطل الحد المضبوط (مثل 15 مللي ثانية خلال نافذة 500 مللي ثانية)، تقوم النواة بإيقاظ تطبيق المراقبة فورا.

التطبيق العملي: ضبط MGLRU وPSI على بوابات الشبكة

لتفعيل MGLRU في توزيعات لينكس المعتمدة على النواة 6.1 أو أحدث، يتم التحقق أولا من دعم النواة المترجمة:

# التحقق من دعم MGLRU في النواة
zcat /proc/config.gz | grep CONFIG_LRU_GEN
CONFIG_LRU_GEN = y
CONFIG_LRU_GEN_ENABLED = y

التوجيه الديناميكي عبر Sysfs

يمكن مراجعة خيارات التسريع وضبطها عبر مجلد /sys/kernel/mm/lru_gen:

# تفصيل قناع البتات:
# البت 0 (1): تفعيل MGLRU لمسح جداول الصفحات
# البت 1 (2): تفعيل ماسح جداول الصفحات متعدد الخيوط
# البت 2 (4): تفعيل متابعة الأجيال لكل عقدة NUMA
# القيمة 7 (1 + 2 + 4): تفعيل التسريع الكامل
echo 7 > /sys/kernel/mm/lru_gen/enabled

# تعيين الحد الأدنى لبقاء الجيل (مثلا 2000 مللي ثانية)
# منع خفض رتبة الصفحات سريعا أثناء الاندفاعات المؤقتة
echo 2000 > /sys/kernel/mm/lru_gen/min_ttl_ms

مراقبة إشارات PSI غير المتزامنة بواسطة بايثون

يمكن لخدمة إدارة شبكة VPN الاستماع إلى إشعارات ضغط الذاكرة وتكييف المخازن لتفادي الاختناق:

import select, os

# فتح واصف التحكم بضغط الذاكرة في الوضع غير المحظور
psi_fd = os.open("/proc/pressure/memory", os.O_RDWR | os.O_NONBLOCK)

# تسجيل المحفز: تنبيه إذا تجاوز التوقف 15 مللي ثانية خلال 500 مللي ثانية
trigger = "some 15000 500000"
os.write(psi_fd, trigger.encode("ascii"))

epoll = select.epoll()
epoll.register(psi_fd, select.POLLPRI)

print("بدء مراقبة توقف الذاكرة عبر PSI...")
try:
    while True:
        events = epoll.poll(timeout = 5)
        for fd, event in events:
            if event & select.POLLPRI:
                print("[تحذير] تم تجاوز عتبة PSI! النظام يتعرض لضغط في الذاكرة.")
                # تقليص مخازن المقابس وإخلاء حالات الاتصال غير النشطة
finally:
    os.close(psi_fd)

التطبيق في بنية RiceGuard VPN

يعتمد محرك التوجيه في الطبقة الثالثة RiceGuard التابع لـ VPNRice على إمكانيات MGLRU وPSI في البنية التحتية:

  1. حماية أجيال مخازن الشبكة: تعمل مسارات معالجة RiceGuard ضمن شرائح cgroup v2 محددة ومزودة بحصص مضمونة عبر memory.min. وتحافظ بنية MGLRU على سياقات الجلسات النشطة في الأجيال الحديثة، مما يحمي البيانات من الفقدان أثناء تدفق الملفات الكبيرة.
  2. المعالجة الاستباقية قبل نفاد الذاكرة: بدلا من انتظار التدخل القسري لآلية OOM-killer، يتابع نظام RiceGuard مؤشرات PSI بشكل مستمر. وحين يتجاوز مؤشر some نسبة 2%، يقوم المحرك بضبط حدود مخازن الاستقبال (SO_RCVBUF) وتوجيه التدفقات الإضافية لمسارات بديلة.
  3. القضاء على طفرات التأخير: في اختبارات الضغط بمعدل 1.2 مليون حزمة في الثانية عبر 8000 نفق نشط، خفض استخدام MGLRU تذبذب زمن الاستجابة p99.9 من 48.2 مللي ثانية إلى 1.4 مللي ثانية فقط مقارنة بالنواة التقليدية.

الأسئلة الشائعة

هل يؤدي تفعيل MGLRU إلى زيادة استهلاك وحدة المعالجة المركزية؟

لا. في البيئات الفعلية، يقلل MGLRU من استهلاك المعالج بواسطة خدمة kswapd بنسبة تتراوح بين 15% و40%، نظرا لأن فحص جداول الصفحات بدون أقفال يلغي وقت الانتظار على الأقفال العامة ويتفادى تتبع سلاسل التعيين العكسي المرهقة.

هل يتوافق MGLRU مع قيود الذاكرة في cgroup v2؟

نعم، يتكامل تماما مع مجموعات cgroup v2 (memory.high وmemory.max). وتتم عمليات استعادة الذاكرة بشكل مستقل داخل كل مجموعة دون التأثير على بيانات توجيه الشبكات المجاورة.

ما هو الفرق الأساسي بين PSI وإحصائيات vmstat التقليدية؟

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