گیت‌وی‌های سازمانی VPN و رله‌های لبه‌ای توزیع‌شده بر روی سرورهای چند‌هسته‌ای پرتراکم با ۶۴، ۱۲۸ یا ۲۵۶ رشته پردازشی فیزیکی فعالیت می‌کنند. هنگامی که پای پردازش ترافیک انبوه با نرخ ده‌ها گیگابیت در ثانیه روی صدها هزار تونل رمزگذاری‌شده هم‌زمان در میان است، توان عملیاتی شبکه به‌ندرت با محدودیت سرعت کلاک پردازنده روبه‌رو می‌شود. در عوض، افت عملکرد ناشی از تنگناهای لایه حافظه رخ می‌دهد: جهش خطوط حافظه پنهان (cache-line bouncing)، ازدحام در گذرگاه‌های ارتباطی نودهای NUMA و بار اضافی ناشی از وقفه‌های میان‌پردازنده‌ای (IPI).

در تنظیمات پیش‌فرض هسته لینوکس، بسته‌های شبکه‌ای که به کارت‌های شبکه چندصفی (multi-queue NIC) وارد می‌شوند، ابتدا در مرحله softirq روی یک هسته پردازشی بررسی شده و سپس برای مصرف به رشته کاری در فضای کاربر تحویل داده می‌شوند که روی هسته‌ای کاملاً متفاوت در حال اجراست. این ناهماهنگی ساختاری موجب ابطال حافظه پنهان سطح L1 و L2 به ازای هر بسته می‌گردد و گذرگاه حافظه را به همگام‌سازی مداوم داده‌ها میان هسته‌ها وامی‌دارد.

برای دستیابی به مقیاس‌پذیری خطی و تثبیت تأخیر انتهایی در بازه زیر یک میلی‌ثانیه، رله‌های لبه‌ای پرسرعت از گزینه‌های تخصصی سوکت در لینوکس بهره می‌گیرند: SO_INCOMING_CPU و SO_INCOMING_NAPI_ID. این سازوکارها با پیوند دادن مستقیم سوکت به صف دریافت سخت‌افزاری و هسته مجری softirq، محلی‌سازی حافظه پنهان را بدون نیاز به ارتباطات بین‌فرایندی فراهم می‌سازند و سرورهای چند‌هسته‌ای را به بسترهایی کاملاً همروند و بدون قفل تبدیل می‌کنند.


سد مقیاس‌پذیری چند‌هسته‌ای: تداخل حافظه پنهان و هزینه‌های میان‌هسته‌ای

برای درک دلایل افت توان عملیاتی در سرورهای چند‌هسته‌ای، بررسی مسیر فیزیکی پیموده شده توسط یک بسته در ساختار سرور ضروری است:

  1. ورود سخت‌افزاری (Hardware Ingress): بسته رمزنگاری‌شده به کارت شبکه وارد می‌شود. ماژول Receive-Side Scaling یا همان RSS بر اساس فیلدهای سرایند یک هش Toeplitz محاسبه کرده و فریم داده را به یکی از صف‌های سخت‌افزاری دریافت هدایت می‌کند (برای نمونه صف RX شماره ۳).
  2. ارسال وقفه و چرخه NAPI: کارت شبکه وقفه MSI-X را صادر نموده و اجرای کد را به هسته ۳ می‌سپارد. چرخه NAPI هسته در فرآیند ksoftirqd/3 به تحلیل سرایندها، ارزیابی وضعیت رمزنگاری و شناسایی سوکت مقصد می‌پردازد.
  3. خروج از صف و پردازش در فضای کاربر: اگر دیمن فضای کاربر بدون در نظر گرفتن همبستگی هسته از epoll استاندارد استفاده کند، یک رشته کاری تصادفی روی هسته ۱۱ بیدار شده و بسته را از طریق فراخوانی recvmsg() دریافت می‌کند.
تداخل و ابطال حافظه پنهان (در غیاب همبستگی با هسته پردازشی):

کارت شبکه فیزیکی (صف دریافت RSS شماره ۳)
         │ (وقفه سخت‌افزاری MSI-X)
         ▼
[ هسته CPU ۳: چرخه Softirq / NAPI ]    <── داده‌های بسته در حافظه پنهان L1/L2 هسته ۳ ثبت می‌شود
         │
         │ (تحویل میان‌هسته‌ای / بیداری Epoll)
         ▼
[ هسته CPU ۱۱: رشته کاری فضای کاربر ]  <── خطای حافظه پنهان سرد! خواندن اجباری از طریق
                                            گذرگاه حافظه از هسته ۳ یا رم اشتراکی L3.

زمانی که داده‌ها و توصیف‌کننده‌های نوشته‌شده توسط هسته ۳ توسط هسته ۱۱ خوانده می‌شوند، سخت‌افزار ناچار است پروتکل‌های همگرایی حافظه پنهان (مانند MESI یا MOESI) را روی گذرگاه پردازنده یا لینک‌های AMD Infinity Fabric و Intel UPI فعال کند. در سروری با نرخ پردازش ۵ میلیون بسته در ثانیه، این فرآیند ابطال متقابل، پهنای باند گذرگاه را اشباع ساخته و تأخیر دم یا p99 را بیش از ۴۰۰ درصد افزایش می‌دهد.


بررسی عمیق: SO_INCOMING_CPU و همبستگی با Softirq هسته

قابلیت SO_INCOMING_CPU که در لینوکس 3.19 معرفی شد، کنترل دقیقی روی تخصیص هسته به برنامه‌های کاربردی اعطا می‌کند. این ویژگی در دو حالت ارزیابی سوکت و هدایت اتصالات جدید عمل می‌نماید.

1. هدایت مستقیم اتصالات به هسته‌های پردازش‌کننده Softirq

با به‌کارگیری هم‌زمان SO_REUSEPORT و SO_INCOMING_CPU، تخصیص اتصالات کاملاً قطعی و پیش‌بینی‌پذیر می‌شود. به‌جای توزیع تصادفی تقاضاهای اتصال TCP یا جریان‌های UDP میان کلیه سوکت‌های شنونده، برنامه اقدام به ایجاد سوکت‌های شنونده مجزا می‌کند، به‌گونه‌ای که هر سوکت به یک هسته پردازشی فیزیکی متصل است:

int cpu_id = 4;
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);

int enable_reuseport = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, &enable_reuseport, sizeof(enable_reuseport));

/* دستور به هسته برای هدایت اتصالات پردازش‌شده در هسته ۴ به این سوکت */
setsockopt(listen_fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu_id, sizeof(cpu_id));

bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(listen_fd, 1024);

در لحظه ورود بسته، روتین جستجوی سوکت در هسته مطابقت شناسه هسته مجری (smp_processor_id()) با سوکت‌های موجود در آرایه SO_REUSEPORT را بررسی می‌کند. در صورت تطابق، تقاضای اتصال مستقیماً درون صف پذیرش همان سوکت قرار می‌گیرد.

2. حذف وقفه‌های میان‌پردازنده‌ای و جهش‌های حافظه پنهان L3

زمانی که رشته‌های کاری با استفاده از pthread_setaffinity_np() اجرای خود را به هسته‌های متناظر مقید می‌کنند، عملیات خواندن تنها از صف‌های محلی انجام می‌پذیرد:

  • رشته پردازشی ۴ به هسته ۴ متصل است.
  • هسته ۴ داده‌های دریافتی از شبکه را در قالب softirq مدیریت می‌کند.
  • رشته ۴ داده‌ها را از سوکتی با تنظیم SO_INCOMING_CPU = 4 استخراج می‌نماید.

به این علت که ساختار داده‌ها در حافظه پنهان سطوح L1 و L2 هسته ۴ باقی می‌ماند، زمان دستیابی به حافظه از حدود ۶۰ نانوثانیه به کمتر از ۲ نانوثانیه کاهش می‌یابد. وقفه‌های میان‌پردازنده‌ای (IPI) نیز به‌طور کامل از مسیر اصلی ترافیک حذف می‌شوند.


هماهنگی با صف‌های سخت‌افزاری: استعلام SO_INCOMING_NAPI_ID

در حالی که SO_INCOMING_CPU اتصال را در سطح هسته‌ها ساماندهی می‌کند، کارت‌های شبکه مدرن حجم کار را بین چندین صف سخت‌افزاری تقسیم می‌کنند. هر صف فیزیکی با یک نمونه اختصاصی NAPI در هسته همخوانی دارد.

1. نگاشت حلقه‌های دریافت کارت شبکه به رشته‌های پردازشی فضای کاربر

در لینوکس 4.12، زیرسیستم شبکه گزینه فقط‌خواندنی SO_INCOMING_NAPI_ID را ارائه کرد. این قابلیت به فرایندهای فضای کاربر اجازه می‌دهد شماره دقیق صف سخت‌افزاری پردازش‌کننده یک اتصال را به دست آورند:

unsigned int napi_id = 0;
socklen_t optlen = sizeof(napi_id);

if (getsockopt(client_fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &napi_id, &optlen) == 0) {
    /* انتقال client_fd به رشته کاری اختصاص‌یافته برای این حلقه NAPI */
    assign_socket_to_napi_worker(client_fd, napi_id);
}

با استعلام NAPI ID، گیت‌وی‌های با کارایی بالا می‌توانند ارتباطی یک‌به‌یک میان صف‌های سخت‌افزاری و چرخه‌های پردازش رویداد برقرار سازند. چنانچه یک کارت شبکه ۱۰۰ گیگابیتی دارای ۳۲ صف دریافت باشد، برنامه ۳۲ رشته کاری مستقل را به هر یک از این صف‌ها تخصیص می‌دهد.

2. مسیرهای متقارن دوطرفه بسته‌ها با ادغام قابلیت XPS

انتقال پرسرعت نیازمند تقارن ساختاری در هر دو مسیر دریافت (RX) و ارسال (TX) است. چنانچه پروکسی پس از رمزگشایی بسته، آن را از طریق هسته‌ای نامرتبط به رابط داخلی ارسال کند، رقابت بر سر قفل‌های صفوف خروجی شکل می‌گیرد.

مسیر سخت‌افزاری متقارن سرتاسری:

[ صف RX کارت شبکه ۲ ] ──► [ Softirq هسته ۲ ] ──► [ رشته کاری ۲ (SO_INCOMING_CPU) ]
                                                              │
                                                              ▼ (پردازش و رمزگشایی)
[ صف TX کارت شبکه ۲ ] ◄── [ صف XPS هسته ۲ ] ◄── [ بافر ارسال خروجی (بدون رقابت قفل) ]

با تلفیق SO_INCOMING_NAPI_ID در ورود و سیستم هدایت بسته‌های ارسالی (XPS) بر روی همان هسته در خروج، بسته در یک کانال یکپارچه از کابل تا فضای کاربر و مجدداً به سمت کابل حرکت می‌کند، بدون اینکه خطوط میان‌هسته‌ای را پشت سر بگذارد.


معماری پیاده‌سازی: حلقه‌های کاری متصل به هسته و تفکیک سوکت‌ها

استقرار SO_INCOMING_CPU در سامانه‌های رویدادمحور مستلزم سازماندهی گام‌به‌گام است:

  1. شناسایی توپولوژی: بررسی مسیرهای /sys/devices/system/cpu/ و پارامترهای NUMA برای شناخت ساختار هسته‌های فیزیکی.
  2. آماده‌سازی سوکت‌های شنونده: ایجاد آرایه‌ای از سوکت‌ها با فلگ‌های SO_REUSEPORT و SO_INCOMING_CPU منطبق با تک‌تک هسته‌ها.
  3. تثبیت هسته پردازشی: فراخوانی تابع pthread_setaffinity_np() در ابتدای هر رشته کاری جهت انحصار اجرا بر روی هسته مربوطه.
  4. ثبت در Epoll: افزودن سوکت شنونده انحصاری و سوکت‌های پذیرفته‌شده مرتبط صرفاً در نمونه محلی epoll_create1().
/* نقطه ورود رشته پردازشی */
void *worker_loop(void *arg) {
    int core_id = (int)(intptr_t)arg;
    
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
    
    /* چرخه epoll اختصاصی این هسته */
    int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listeners[core_id], &ev);
    
    while (running) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        for (int i = 0; i < nfds; i++) {
            /* پردازش اتصالات فعال در حافظه پنهان L1/L2 محلی */
            handle_active_socket(events[i].data.fd);
        }
    }
    return NULL;
}

تحلیل بنچمارک‌های خرد: پایداری تأخیر و افزایش توان عملیاتی

آزمون‌های مقایسه‌ای بر روی سرورهای ۱۲۸ هسته‌ای AMD EPYC تحت ترافیک مداوم ۲۰ گیگابیت بر ثانیه، مزایای قطعی اتصال سوکت به هسته را نشان می‌دهد:

ارزیابی عملکرد در زیر بار سنگین چند‌هسته‌ای:

معیار ارزیابی                 Epoll استاندارد      SO_INCOMING_CPU + انتساب NAPI
────────────────────────────────────────────────────────────────────────────────
توان عملیاتی (Mpps)           3.4 Mpps             5.8 Mpps (+70%)
خطای حافظه پنهان L3 (Misses)  28.4%                6.2% (-78%)
وقفه‌های میان‌پردازنده‌ای      142,000 / ثانیه      1,800 / ثانیه (-98%)
تأخیر انتهایی (p99)           14.2 ms              1.1 ms (-92%)
بار سیستمی هسته (IPI/Lock)    34%                  7%

با رفع ابطال متقابل حافظه پنهان، مصرف پهنای باند گذرگاه پردازنده به شکل محسوسی کاهش می‌یابد و سیستم قادر خواهد بود بدون از دست رفتن بسته‌ها در صفوف راه‌انداز کارت شبکه، امواج ناگهانی ترافیک را مهار کند.


مسیر داده RiceGuard: تله‌متری مجزای هسته و انتقال در نرخ خط شبکه

سامانه VPNRice ویژگی‌های SO_INCOMING_CPU و SO_INCOMING_NAPI_ID را درون لایه انتقال داده رله‌های RiceGuard تعبیه کرده است. در میزبانی ارتباطات پرسرعت سازمانی و نودهای لبه‌ای، تضمین سرعت پایدار هدایت داده پیش‌شرطی اساسی در ایمنی شبکه به شمار می‌آید.

در رله‌های RiceGuard، بسته‌های ورودی ابتدا در فاز XDP پالایش شده و سپس مستقیماً در اختیار پردازش‌های فضای کاربر تثبیت‌شده در حافظه NUMA قرار می‌گیرند. این تطابق دقیق میان صفوف سخت‌افزاری و چرخه‌های کاری، قفل‌های همگام‌سازی و تغییرات زمینه پردازنده را مرتفع می‌سازد. جداسازی ساختاری موجب می‌شود که هجوم درخواست‌های نوپا خللی در نشست‌های جاری ایجاد ننماید و بستری امن، کم‌تأخیر و حافظ حریم خصوصی دیجیتال فراهم آورد.